forked from mirrors/cobblemon
Compare commits
507
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
aeedec0b6d | ||
|
|
a37fdf709a | ||
|
|
5023f28d04 | ||
|
|
67de28386a | ||
|
|
cd37d3538e | ||
|
|
3d03ee5eb3 | ||
|
|
0b685ed453 | ||
|
|
115e990df6 | ||
|
|
2ee38f0a29 | ||
|
|
756ef48ba2 | ||
|
|
2c4df6622a | ||
|
|
425c41bc4b | ||
|
|
9f7a99b583 | ||
|
|
f1213abdf4 | ||
|
|
9b1d847f59 | ||
|
|
bf900a0167 | ||
|
|
d60d828e93 | ||
|
|
703282eca1 | ||
|
|
5d7e6f6487 | ||
|
|
4e3e6405d0 | ||
|
|
1e161a52ee | ||
|
|
65b2204783 | ||
|
|
76d9137ae4 | ||
|
|
9f6c44fd75 | ||
|
|
6c086f5555 | ||
|
|
a9115bfb15 | ||
|
|
f2c5113f0b | ||
|
|
e29a2402e9 | ||
|
|
aa4857abbb | ||
|
|
d29653233d | ||
|
|
1b51a128d7 | ||
|
|
245011cf5e | ||
|
|
100bf7fee4 | ||
|
|
be043a0ea1 | ||
|
|
a902f4a8a3 | ||
|
|
442f54839f | ||
|
|
20a97984a2 | ||
|
|
d1517548bd | ||
|
|
3e1f9dc9b0 | ||
|
|
fd19e65f79 | ||
|
|
6b10d81bc8 | ||
|
|
f3b9c03fc2 | ||
|
|
92653c1307 | ||
|
|
c372114da5 | ||
|
|
2b95138c46 | ||
|
|
864e6be461 | ||
|
|
c02fa1f85e | ||
|
|
503b1069fd | ||
|
|
f1d593d53c | ||
|
|
55dd486044 | ||
|
|
3263837137 | ||
|
|
a471fcd948 | ||
|
|
a14bfd55d1 | ||
|
|
725e819e89 | ||
|
|
b52b843206 | ||
|
|
74c65e22cb | ||
|
|
c348520a9b | ||
|
|
78dd5f334a | ||
|
|
67318c9307 | ||
|
|
31c48407df | ||
|
|
43da7ecece | ||
|
|
0957de86c7 | ||
|
|
daae444fa9 | ||
|
|
79423ef5ee | ||
|
|
6255f7dc31 | ||
|
|
ff10306372 | ||
|
|
aaf7daa45b | ||
|
|
ae550f78c4 | ||
|
|
66e4194a3e | ||
|
|
12e6b76504 | ||
|
|
93861af71f | ||
|
|
0c80748012 | ||
|
|
fcf832c22c | ||
|
|
a065aff744 | ||
|
|
d77f424a83 | ||
|
|
d5d2a2cec3 | ||
|
|
036e150191 | ||
|
|
0bf1d84675 | ||
|
|
b1b8e658b2 | ||
|
|
f984b21929 | ||
|
|
0a5193b209 | ||
|
|
43f1f1c30d | ||
|
|
c16597ae41 | ||
|
|
f76e7582d2 | ||
|
|
40761d87db | ||
|
|
89a9ad0bfb | ||
|
|
20291c54a5 | ||
|
|
d6fdfe6ebc | ||
|
|
9f63b62651 | ||
|
|
392632b7d4 | ||
|
|
0d377dcbe0 | ||
|
|
88c31bcb7b | ||
|
|
8efea17ae6 | ||
|
|
cdbf569bbc | ||
|
|
8b81b13d01 | ||
|
|
f19d9410c0 | ||
|
|
9ac3a9249d | ||
|
|
4f340b2eee | ||
|
|
0415cf8a69 | ||
|
|
79af092888 | ||
|
|
5cff52df5f | ||
|
|
ca311fa97c | ||
|
|
27020d3068 | ||
|
|
3ac4b8622f | ||
|
|
acd7a0583c | ||
|
|
e341948bd4 | ||
|
|
1a465abf9d | ||
|
|
e56a6e5b1a | ||
|
|
33d707a73b | ||
|
|
e3c3cd380c | ||
|
|
578ce789b1 | ||
|
|
460e8f7501 | ||
|
|
2fee86e7bc | ||
|
|
f73e631f81 | ||
|
|
0ca0c1a552 | ||
|
|
b3bf2c5ded | ||
|
|
7875006c8e | ||
|
|
67bb63b638 | ||
|
|
511f97b963 | ||
|
|
54c426f5ff | ||
|
|
f89902ece8 | ||
|
|
52c6bbd842 | ||
|
|
8246a822cf | ||
|
|
54202bed98 | ||
|
|
e6313cf4c5 | ||
|
|
7debc6e047 | ||
|
|
bf9b69a143 | ||
|
|
d7f34972c1 | ||
|
|
fa7356d2ed | ||
|
|
812d42a42f | ||
|
|
3f114706fa | ||
|
|
8d1f41919d | ||
|
|
b40c21d99e | ||
|
|
461d8bd282 | ||
|
|
84d34709a3 | ||
|
|
b6c04e8746 | ||
|
|
81a96afaa6 | ||
|
|
724332dd34 | ||
|
|
f5bfd64495 | ||
|
|
bf8f7f628f | ||
|
|
898fdf983f | ||
|
|
a547e0f535 | ||
|
|
564ced926c | ||
|
|
a93dd14fd8 | ||
|
|
f0535901cb | ||
|
|
7a42df591f | ||
|
|
616419880f | ||
|
|
9fa38430c0 | ||
|
|
26012278e7 | ||
|
|
2c0967d293 | ||
|
|
885fc72d93 | ||
|
|
8130cd0998 | ||
|
|
447d35f28b | ||
|
|
bf82def925 | ||
|
|
dace364701 | ||
|
|
d737444ac9 | ||
|
|
95f58ba557 | ||
|
|
8993b007a5 | ||
|
|
c84f9dd112 | ||
|
|
eea7477f7f | ||
|
|
241abd79d7 | ||
|
|
35618cea46 | ||
|
|
31b97d391d | ||
|
|
7769a73246 | ||
|
|
65bf9507d3 | ||
|
|
6d336fc27b | ||
|
|
9514a1d470 | ||
|
|
16df7695c4 | ||
|
|
8fd63912a3 | ||
|
|
04941a9a38 | ||
|
|
1853a1677a | ||
|
|
94f8de8ddc | ||
|
|
7ea866d82a | ||
|
|
ea529a52c2 | ||
|
|
1f6dd03064 | ||
|
|
d62ea394ee | ||
|
|
d0b8a3368c | ||
|
|
6d23e762d6 | ||
|
|
6edb0c19c4 | ||
|
|
71e9adae1f | ||
|
|
35b53daa09 | ||
|
|
937c30fca5 | ||
|
|
141249e6c7 | ||
|
|
94f9bf9f50 | ||
|
|
4638b01279 | ||
|
|
e39d59c37a | ||
|
|
072071d679 | ||
|
|
aa4400395b | ||
|
|
09ad3d0a4d | ||
|
|
de7df10f36 | ||
|
|
897bcf6fc0 | ||
|
|
1e14042455 | ||
|
|
8583c9af4d | ||
|
|
ce756997d6 | ||
|
|
4efc5b6748 | ||
|
|
7ed6075410 | ||
|
|
320d995672 | ||
|
|
8ae91587aa | ||
|
|
61362fa1b1 | ||
|
|
d010129864 | ||
|
|
282f002895 | ||
|
|
863f6de059 | ||
|
|
8ddf628f19 | ||
|
|
8762d518c1 | ||
|
|
46798d694e | ||
|
|
056dd0fd4b | ||
|
|
3ac00d976b | ||
|
|
b30a76855b | ||
|
|
e976556ae3 | ||
|
|
9f16eca621 | ||
|
|
f4a94bd732 | ||
|
|
b6c964e03b | ||
|
|
aed2204483 | ||
|
|
3ed46cb7af | ||
|
|
cf9efe9b8c | ||
|
|
02b9431731 | ||
|
|
a30ae0ccef | ||
|
|
a2905f4d37 | ||
|
|
b3aba1532c | ||
|
|
6369bdb612 | ||
|
|
b5db5605ef | ||
|
|
0207459a0f | ||
|
|
0e2fdfbce8 | ||
|
|
8c9da885c7 | ||
|
|
a5b6529f3c | ||
|
|
6e9bdb6019 | ||
|
|
b430b7db76 | ||
|
|
8015ff0e20 | ||
|
|
ee78657e3d | ||
|
|
6cc84cd38c | ||
|
|
31d6ff541e | ||
|
|
841a40c973 | ||
|
|
e1a4086f24 | ||
|
|
df049a315c | ||
|
|
9ce5486189 | ||
|
|
b1992a94d3 | ||
|
|
167a1ea8eb | ||
|
|
f95beaa7a1 | ||
|
|
1c0f7bbf8f | ||
|
|
34cc2cda46 | ||
|
|
696e58bd1a | ||
|
|
10412b80ca | ||
|
|
458c2ce0ed | ||
|
|
6182715644 | ||
|
|
b41c4c21a8 | ||
|
|
e1893f50ae | ||
|
|
a1bdf3ded6 | ||
|
|
a458b31c9f | ||
|
|
03b5336eb8 | ||
|
|
a270371632 | ||
|
|
a9caf92d2f | ||
|
|
9e2d00df63 | ||
|
|
e8003bb29a | ||
|
|
a248d661fa | ||
|
|
59390f4d84 | ||
|
|
99b7e4a89e | ||
|
|
fb37e543cf | ||
|
|
c0c75a9735 | ||
|
|
3febee21b6 | ||
|
|
c3a017687c | ||
|
|
afd082c966 | ||
|
|
a6a1f7b75b | ||
|
|
72738a45e6 | ||
|
|
ff4d9ae208 | ||
|
|
a87d522f6d | ||
|
|
8a0076acb4 | ||
|
|
f7e1def555 | ||
|
|
c13f6ae627 | ||
|
|
c24ab2049d | ||
|
|
b7b1850a4e | ||
|
|
8995c107f7 | ||
|
|
dca6b5273d | ||
|
|
d6a412b91a | ||
|
|
2e8483cdc7 | ||
|
|
5984d7a126 | ||
|
|
6d8e645092 | ||
|
|
6b1c2e2074 | ||
|
|
8af5b5e07a | ||
|
|
7ae19ca774 | ||
|
|
830f00553e | ||
|
|
55f0db2263 | ||
|
|
411788a07e | ||
|
|
f6151041c5 | ||
|
|
c87f56c83f | ||
|
|
4478532bcb | ||
|
|
f99168f1c3 | ||
|
|
adce1d8838 | ||
|
|
e95c82b5af | ||
|
|
dd273ffef2 | ||
|
|
184348d6fa | ||
|
|
04d288ed7e | ||
|
|
0d5cce730e | ||
|
|
3e01535203 | ||
|
|
f0d99a9d83 | ||
|
|
b4eb46d632 | ||
|
|
7e52336255 | ||
|
|
2b40b5b689 | ||
|
|
da889f220b | ||
|
|
ac65910380 | ||
|
|
ba6847be9a | ||
|
|
5ddbcd1830 | ||
|
|
47605646d0 | ||
|
|
d9fdf089c2 | ||
|
|
41db2a53ce | ||
|
|
85d8779818 | ||
|
|
e9a405694c | ||
|
|
d817ce1471 | ||
|
|
ce09b66db5 | ||
|
|
d0c666a20d | ||
|
|
04cef915e4 | ||
|
|
e50412443e | ||
|
|
3fe1f87f18 | ||
|
|
5a0fc199ff | ||
|
|
beb769c2a5 | ||
|
|
a7b6fae4ed | ||
|
|
af2ff7bbfa | ||
|
|
4f3575b2c6 | ||
|
|
8555168a6a | ||
|
|
98c248ef13 | ||
|
|
4a2c69d47c | ||
|
|
1b5c0be975 | ||
|
|
5924398eff | ||
|
|
a08855ab37 | ||
|
|
91abe39cdd | ||
|
|
ad8aab7c19 | ||
|
|
aa6b196c29 | ||
|
|
c23cb644d9 | ||
|
|
a8b12cfca3 | ||
|
|
5268447fa3 | ||
|
|
b489e747bb | ||
|
|
08f957e33b | ||
|
|
e17d38c53a | ||
|
|
818b39e945 | ||
|
|
0397e39735 | ||
|
|
6ca1d23f45 | ||
|
|
30666349a1 | ||
|
|
44def2be76 | ||
|
|
507f70c2d5 | ||
|
|
d91118d05f | ||
|
|
07d8e6ed09 | ||
|
|
fb34d4bd23 | ||
|
|
6ca1337ff9 | ||
|
|
98106cad12 | ||
|
|
0d344337dd | ||
|
|
64d160020b | ||
|
|
fb4c80f68c | ||
|
|
3d27681aaa | ||
|
|
2b5433f9da | ||
|
|
352202203a | ||
|
|
62cfbd7087 | ||
|
|
5a64e8ea1b | ||
|
|
8dc9863a5d | ||
|
|
9bad141c3f | ||
|
|
9280258413 | ||
|
|
51a11163dd | ||
|
|
fe8e4dcaff | ||
|
|
e0bd6766fc | ||
|
|
4da800d396 | ||
|
|
f3246ae14a | ||
|
|
82548b5383 | ||
|
|
d5ad84a495 | ||
|
|
ad73202e17 | ||
|
|
1560b142d2 | ||
|
|
47f87b1be3 | ||
|
|
2965a04742 | ||
|
|
b01086270a | ||
|
|
da6a09c942 | ||
|
|
8d5eb5883d | ||
|
|
d5fa693f43 | ||
|
|
0d12bf1190 | ||
|
|
4f773f63cb | ||
|
|
3f3ae74535 | ||
|
|
7429fea802 | ||
|
|
ac5cd47715 | ||
|
|
9901d39f9b | ||
|
|
c635a1d331 | ||
|
|
cf2173a281 | ||
|
|
710d37f453 | ||
|
|
1fb3416211 | ||
|
|
f49f2a7305 | ||
|
|
b0e9c1ab28 | ||
|
|
ac1c7d8b78 | ||
|
|
dec5151eb8 | ||
|
|
196edc2711 | ||
|
|
18015a26fd | ||
|
|
aa296a79a9 | ||
|
|
92dd85643e | ||
|
|
8849de1bfd | ||
|
|
2a349697b4 | ||
|
|
a4055e6206 | ||
|
|
03bda4e27d | ||
|
|
5d8abbd4c7 | ||
|
|
6b167a7a17 | ||
|
|
5c64bc94ff | ||
|
|
d36c24075c | ||
|
|
d1a8122296 | ||
|
|
a8e6e684ab | ||
|
|
188fc2b5d9 | ||
|
|
2bc2e3f020 | ||
|
|
7a15e6b004 | ||
|
|
6ee6beddeb | ||
|
|
b92960da77 | ||
|
|
99618184e7 | ||
|
|
fcdff126fd | ||
|
|
3b285cc8bd | ||
|
|
4dead98e6b | ||
|
|
db36f8da2a | ||
|
|
3a448071d7 | ||
|
|
7215860d74 | ||
|
|
dc899ffc1b | ||
|
|
88fe55bbb9 | ||
|
|
3d3c421b5c | ||
|
|
3c2cfa4ffc | ||
|
|
89a4edb327 | ||
|
|
ab37580258 | ||
|
|
c116c01fd6 | ||
|
|
e3ad8f8079 | ||
|
|
a491d508b6 | ||
|
|
3e11d0550f | ||
|
|
e8b50f8115 | ||
|
|
02fb825328 | ||
|
|
8df6eea107 | ||
|
|
1376bb68f4 | ||
|
|
beb8f2ccf1 | ||
|
|
510dfec0e5 | ||
|
|
e54932c876 | ||
|
|
dd14c3bbdd | ||
|
|
5ea237f8a0 | ||
|
|
fb154ba457 | ||
|
|
f7812896a8 | ||
|
|
b0d57dd875 | ||
|
|
6c09e2b553 | ||
|
|
593104fb0a | ||
|
|
e314664731 | ||
|
|
d0a38a9704 | ||
|
|
6e155d7651 | ||
|
|
ae8f8a2fb8 | ||
|
|
5a6a83aac1 | ||
|
|
e8236b55a4 | ||
|
|
2ea07899ea | ||
|
|
d0ce03cd5e | ||
|
|
8ae0c409a3 | ||
|
|
2782cb5f34 | ||
|
|
2f10707211 | ||
|
|
f40a9d762a | ||
|
|
17587f97e5 | ||
|
|
5230820657 | ||
|
|
61c55e136f | ||
|
|
22cced74f5 | ||
|
|
3cd2dec8e0 | ||
|
|
cea930893a | ||
|
|
8662364ca8 | ||
|
|
1fd079b1b4 | ||
|
|
244876a26c | ||
|
|
e7790f530f | ||
|
|
04a9ab0e67 | ||
|
|
3f338d27c3 | ||
|
|
5da3dfb451 | ||
|
|
fd329e00cd | ||
|
|
6da47a8b2c | ||
|
|
860c73fac9 | ||
|
|
e26fdd4bdd | ||
|
|
8e642f71b8 | ||
|
|
f17884b4e1 | ||
|
|
c40b456948 | ||
|
|
7e74e85315 | ||
|
|
21fd4d7cbf | ||
|
|
908bff9892 | ||
|
|
fa14f70bbb | ||
|
|
188ae2ac85 | ||
|
|
346559f292 | ||
|
|
331b282fcf | ||
|
|
7f97eeafed | ||
|
|
51f5a1f5a8 | ||
|
|
7c0535b8bd | ||
|
|
4f15ac0cbc | ||
|
|
35a0d8a80d | ||
|
|
c0270d3ef0 | ||
|
|
82a1eb2a55 | ||
|
|
a528ea952a | ||
|
|
af2df51a2a | ||
|
|
95a0f9603c | ||
|
|
d38aa6b1a2 | ||
|
|
4566e1b33f | ||
|
|
f2d16ca9b7 | ||
|
|
d6fbb70db0 | ||
|
|
150a5c668a | ||
|
|
1bf01a2826 | ||
|
|
4d1bf677a1 | ||
|
|
069f56acfd | ||
|
|
19cdc8fbef | ||
|
|
1408b94f06 | ||
|
|
de081dc514 | ||
|
|
1538f12934 | ||
|
|
857f67604c | ||
|
|
ae49e3f977 | ||
|
|
ec01a03820 | ||
|
|
96cc3671da | ||
|
|
ad7c9c560c | ||
|
|
ee15ff6b35 | ||
|
|
ca83103703 | ||
|
|
1aeb894b09 | ||
|
|
8e9ab26963 | ||
|
|
44b4fbcfe3 | ||
|
|
42cc1dd8a9 | ||
|
|
d69310e52c | ||
|
|
a0f89274d1 |
+4
-1
@@ -33,4 +33,7 @@ common/src/main/resources/assets/cobblemon/showdown
|
||||
# Generated files
|
||||
*/generated/resources/*
|
||||
/fabric/src/generated/resources/cobblemon-common.accesswidener
|
||||
/.ignored/
|
||||
/.ignored/
|
||||
|
||||
# MacOS
|
||||
.DS_Store
|
||||
+146
-1
@@ -1,5 +1,6 @@
|
||||
# Changelog
|
||||
## [1.5.0](#1-5-0)
|
||||
|
||||
### Additions
|
||||
- Added over (number here) ruin structures, where you can find Pokémon-themed Armor Trims and Pottery Sherds, Tumblestones, and more.
|
||||
- Added (number here) fossil structures, where you can brush Suspicious Sand/Gravel for Fossils.
|
||||
@@ -14,15 +15,133 @@
|
||||
- Added the hidden "True Vivillionaire" advancement.
|
||||
- The "Poké Ball" Vivillon variant is now obtainable by evolving a Spewpa after obtaining the "Aspiring Vivillionaire" advancement.
|
||||
- Added dynamic lighting support for any Species or Forms when using [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) or [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), at this time we don't ship with any defaults for this feature but addon creators can make use of this feature.
|
||||
- Added a Display Case block! Use it to display your archaeological findings, or other items.
|
||||
- Added 6 Pokémon-themed Pottery Sherds, obtained from ruin structures.
|
||||
- Added a Pokémon-themed armor trim, obtained from ruin structures.
|
||||
- You can now find Nether Fire Stone Ore in, surprisingly, the Nether.
|
||||
- You'll also find Terracotta Sun Stone Ore in Badlands biomes.
|
||||
- Added effects for many Poké Balls when sending out Pokémon or capturing.
|
||||
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
|
||||
- Added simple move animations for Pokémon in battle with a proof of concept for more complicated animations.
|
||||
- Added the EV Boosting Feathers.
|
||||
- Added Type Gems.
|
||||
- Added various new tags for searchability and better compatibility.
|
||||
- Added the [Ability Capsule](https://bulbapedia.bulbagarden.net/wiki/Ability_Capsule) and [Patch](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch), these have no acquisition method for now and only work as intended with the traditional ability format of 1 to 2 common abilities and 1 hidden ability.
|
||||
- Added Gilded Chests, available in all Apricorn colors.
|
||||
- Added Relic Coins, Relic Coin Pouches, and Relic Coin Sacks.
|
||||
- Added various new tags for searchability and better compatibility.
|
||||
- Added Type Gems. These items hold no functionality for now. Start collecting though...
|
||||
- Added 6 custom Pottery Sherds, obtained from ruin structures.
|
||||
- Added cries to Meowth, Alolan Meowth, Galaraian Meowth,
|
||||
- Completed the advancement for placing a Tumblestone near Lava or Magma to make it grow.
|
||||
- Added new held items: Ability Shield, Absorb Bulb, Air Balloon, Binding Band, Blunder Policy, Cell Battery, Covert Cloak, Damp Rock, Eject Button, Eviolite, Expert Belt, Float Stone, Focus Sash, Heat Rock, Icy Rock, Iron Ball, Light Ball, Loaded Dice, Red Card, Shell Bell, Sticky Barb, Smooth Rock, Soothe Bell and Weakness Policy.
|
||||
|
||||
### Pokémon Added
|
||||
#### Gen 2
|
||||
- Sentret
|
||||
- Furret
|
||||
- Qwilfish
|
||||
- Heracross
|
||||
- Skarmory
|
||||
- Ferroseed
|
||||
- Ferrothorn
|
||||
|
||||
#### Gen 3
|
||||
- Lileep
|
||||
- Cradily
|
||||
- Anorith
|
||||
- Armaldo
|
||||
- Tropius
|
||||
- Roselia
|
||||
|
||||
#### Gen 4
|
||||
- Shieldon
|
||||
- Bastiodon
|
||||
- Cranidos
|
||||
- Rampardos
|
||||
- Budew
|
||||
- Roserade
|
||||
|
||||
#### Gen 5
|
||||
- Tirtouga
|
||||
- Carracosta
|
||||
- Archen
|
||||
- Archeops
|
||||
- Zorua
|
||||
- Zoroark
|
||||
- Petilil
|
||||
- Lilligant
|
||||
- Darumaka
|
||||
- Darmanitan
|
||||
- Woobat
|
||||
- Swoobat
|
||||
- Sandile
|
||||
- Krokorok
|
||||
- Krookodile
|
||||
- Frillish
|
||||
- Jellicent
|
||||
- Cubchoo
|
||||
- Beartic
|
||||
- Deino
|
||||
- Zweilous
|
||||
- Hydreigon
|
||||
- Larvesta
|
||||
- Volcarona
|
||||
|
||||
#### Gen 6
|
||||
- Tyrunt
|
||||
- Tyrantrum
|
||||
- Amaura
|
||||
- Aurorus
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
|
||||
#### Gen 7
|
||||
- Turtonator
|
||||
- Fomantis
|
||||
- Lurantis
|
||||
- Salandit
|
||||
- Salazzle
|
||||
- Jangmo-o
|
||||
- Hakamo-o
|
||||
- Kommo-o
|
||||
|
||||
#### Gen 8
|
||||
- Gossifleur
|
||||
- Eldegoss
|
||||
- Arctozolt
|
||||
- Arctovish
|
||||
- Dracozolt
|
||||
- Dracovish
|
||||
- Basculegion
|
||||
- Hisuian Zorua
|
||||
- Hisuian Zoroark
|
||||
- Hisuian Voltorb
|
||||
- Hisuian Electrode
|
||||
- Hisuian Qwilfish
|
||||
- Overqwil
|
||||
- Hisuian Sneasel
|
||||
- Sneasler
|
||||
- Stonjourner
|
||||
- Cufant
|
||||
- Copperajah
|
||||
- Dreepy
|
||||
- Drakloak
|
||||
- Dragapult
|
||||
- Hisuian Sliggoo
|
||||
- Hisuian Goodra
|
||||
|
||||
#### Gen 9
|
||||
- Gimmighoul.
|
||||
- Using Relic Coins, you can increase Gimmighoul's Coin Stash. Upon reaching 999, it can be evolved into Gholdengo.
|
||||
- Additionally, if you give Gimmighoul a Netherite Scrap, you can increase its Netherite Stash. Who knows what might happen if it fills up?
|
||||
|
||||
|
||||
### Changes
|
||||
- Pokémon now transition more smoothly between different poses.
|
||||
- Updated models for Mimikyu, Alcremie, Squirtle, Wartortle, Blastoise, Omanyte, Omastar, Growlithe, Swinub, Piloswine, Basculine, Rowlet, Dartrix, Decidueye, Kabuto, Kabutops, Piplup, Prinplup, Elekid, Electabuzz, Cubone, Marowak, Paras, Drifloon, Drifblim, Venonat, Venomoth, Yanma, Yanmega, and Raboot.
|
||||
- Updated animations for Diglett, Dugtrio, Dragonite, Omanyte, Omastar, Pinsir, Falinks, Lechonk, Litwick, Lampent, Chandelure, Carnivine, Galarian Meowth, Galarian Rapidash, Venonat, Venomoth, Phanphy, Donphan, Dratini, Dragonair, Happiny, Chansey, Blissey, Volbeat, Illumise, Numel, Drifloon, and Drifblim.
|
||||
- Added more held items to the held item tag, and Metal Coat to the held item tab.
|
||||
- Added all ores to the modloader ore tags, and added tags for each ore type to the mod (both blocks and items).
|
||||
- Changed the Destiny Knot to be crafted with a Ghast Tear, rather than a diamond.
|
||||
@@ -32,8 +151,17 @@
|
||||
- Updated movesets to add Gen 9 DLC moves.
|
||||
- Apricorn Sprouts can now be planted directly on Apricorn leaves to create a new Apricorn of the same color.
|
||||
- Added tooltips to interaction GUIs to make it clearer what they do.
|
||||
- Poké Ball recipes now use tags for their center ingredient, rather than specificied Copper Ingot, Iron Ingot, Gold Ingot, and Diamond, allowing for further datapack customisation.
|
||||
- Starter Toast now closes once corresponding button is pressed once.
|
||||
- Moved some items in the vanilla Food & Drinks category.
|
||||
- Stacked Cobblemon Potion Bases can now be quick moved into Brewing Stands.
|
||||
- Changed Healer recipe to swap the Max Revive for a Revive.
|
||||
- Upped the rate that Gastly drop Ghast Tears from 2.5% to 5%.
|
||||
- Pokémon with a low Blaze Powder drop chance now drop it more frequently in the Nether.
|
||||
- Reorganised a few advancements.
|
||||
|
||||
### Fixes
|
||||
- Resized Snorlax.
|
||||
- Re-added the recipe for the Iron Vitamin.
|
||||
- Cleaned up empty evolutions declaration in species files (no, I'm not listing all 169 by name).
|
||||
- Fixed Exeggcute not being able to evolve.
|
||||
@@ -46,6 +174,14 @@
|
||||
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
|
||||
- Adds a translation string name to the Empty Pokeball entity.
|
||||
- Fixed very many issues with Bedrock particle effects.
|
||||
- Fixed Arbok patterns not being as specific in spawning as they were intended.
|
||||
- Fixed Pokémon not avoiding danger at all when pathfinding.
|
||||
- Fixed Sobble line not being able to swim or breathe underwater.
|
||||
- Fixed incompatibility with [Just Enough Resources](https://modrinth.com/mod/just-enough-resources-jer).
|
||||
- Fixed incompatibility with [Visual Overhaul](https://modrinth.com/mod/visual-overhaul).
|
||||
- Fixed an issue that allowed Pokemon to spawn on rails.
|
||||
- Fixed the data set on Pokémon when abilities are given via properties such as in ``/spawnpokemon``.
|
||||
- Fixed sometimes Pokémon losing hidden abilities when evolving, this fix will only take effect to newly created Pokémon.
|
||||
|
||||
### Developer
|
||||
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
|
||||
@@ -57,6 +193,13 @@
|
||||
- Made PokemonProperties.parse more interoperable with Java.
|
||||
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
|
||||
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
|
||||
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
|
||||
- ``Pokemon.ability`` no longer has a public setter, please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
|
||||
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
|
||||
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 to 2 regular abilities and 1 hidden.
|
||||
- ``Pokemon#rollAbility`` has been added, this rerolls a legal ability for the Pokémon.
|
||||
- `docs/cobblemon-tags` tags and spawn-presets can now be auto-generated from the current tags in the cobblemon mod with the `generateSpawnPresetList.py` and `generateTagList.py` scripts.
|
||||
- `public_spawns_to_json.py` script has been added to give the powerful spawn.json generator a simplistic UI.
|
||||
|
||||
### Datapack & Resourcepack Creators
|
||||
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
|
||||
@@ -91,6 +234,8 @@
|
||||
}
|
||||
```
|
||||
- Following up on this change ``light_source`` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
|
||||
- Added the item tag ``cobblemon:ability_changers``, contains the ``cobblemon:ability_capsule`` & ``cobblemon:ability_patch`` by default.
|
||||
- Added the item tag ``cobblemon:held/is_friendship_booster``, allows items to give the Soothe Bell effect of a boost of 1.5x Friendship gained, contains ``cobblemon:soothe_bell`` by default.
|
||||
|
||||
## [1.4.1 (December 23rd, 2023)](#1-4-1)
|
||||
|
||||
@@ -1026,4 +1171,4 @@ Thank you so much to all of our community translators that bring the mod to the
|
||||
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
|
||||
|
||||
## [1.0.0 (November 12th, 2022)](#1-0-0)
|
||||
- Initial release.
|
||||
- Initial release.
|
||||
@@ -15,4 +15,7 @@ dependencies {
|
||||
implementation(libs.shadow)
|
||||
implementation(libs.loom)
|
||||
implementation(libs.architecturyPlugin)
|
||||
|
||||
implementation(libs.blossom)
|
||||
implementation(libs.ideaExt)
|
||||
}
|
||||
@@ -1,3 +1,6 @@
|
||||
import extensions.VersionType
|
||||
import extensions.writeVersion
|
||||
|
||||
plugins {
|
||||
java
|
||||
`java-library`
|
||||
@@ -31,7 +34,7 @@ publishing {
|
||||
|
||||
groupId = "com.cobblemon"
|
||||
artifactId = project.findProperty("maven.artifactId")?.toString() ?: project.name
|
||||
version = rootProject.version.toString()
|
||||
version = project.writeVersion(VersionType.PUBLISHING)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package extensions
|
||||
|
||||
import org.gradle.api.Project
|
||||
|
||||
fun Project.version(): String {
|
||||
return rootProject.property("mod_version").toString()
|
||||
}
|
||||
|
||||
fun Project.isSnapshot(): Boolean {
|
||||
return rootProject.property("snapshot") == "true"
|
||||
}
|
||||
|
||||
fun Project.writeVersion(type: VersionType = VersionType.FULL): String {
|
||||
val version = "${rootProject.property("mod_version")}+${rootProject.property("mc_version")}"
|
||||
return when (type) {
|
||||
VersionType.PUBLISHING -> if(this.isSnapshot()) "$version-SNAPSHOT" else version
|
||||
VersionType.FULL -> if(this.isSnapshot()) rootProject.version.toString() else version
|
||||
}
|
||||
}
|
||||
|
||||
enum class VersionType {
|
||||
PUBLISHING,
|
||||
FULL
|
||||
}
|
||||
+2
-2
@@ -9,5 +9,5 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
|
||||
|
||||
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
|
||||
if (isSnapshot) {
|
||||
version = "$version-SNAPSHOT-${versioning.info.branch}-${versioning.info.build}"
|
||||
}
|
||||
version = "$version-${versioning.info.branch}-${versioning.info.build}"
|
||||
}
|
||||
|
||||
+42
-2
@@ -1,3 +1,9 @@
|
||||
import extensions.isSnapshot
|
||||
import extensions.version
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
|
||||
/*
|
||||
*
|
||||
* * Copyright (C) 2023 Cobblemon Contributors
|
||||
@@ -11,6 +17,10 @@
|
||||
plugins {
|
||||
id("cobblemon.base-conventions")
|
||||
id("cobblemon.publish-conventions")
|
||||
|
||||
id("net.kyori.blossom")
|
||||
id("org.jetbrains.gradle.plugin.idea-ext")
|
||||
id ("net.nemerosa.versioning") version "2.8.2"
|
||||
}
|
||||
|
||||
architectury {
|
||||
@@ -44,8 +54,7 @@ dependencies {
|
||||
modCompileOnly(libs.mongoDriverCore)
|
||||
modCompileOnly(libs.mongoDriverSync)
|
||||
|
||||
testRuntimeOnly(libs.junitEngine)
|
||||
testImplementation(libs.junitApi)
|
||||
testImplementation(libs.fabricJunit)
|
||||
testImplementation(libs.junitParams)
|
||||
testImplementation(libs.mockito)
|
||||
testImplementation(libs.mockk)
|
||||
@@ -59,3 +68,34 @@ tasks.withType<Test> {
|
||||
setExceptionFormat("full")
|
||||
}
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
blossom {
|
||||
kotlinSources {
|
||||
fun generateLicenseHeader() : String {
|
||||
val builder = StringBuilder()
|
||||
builder.append("/*\n")
|
||||
rootProject.file("HEADER").forEachLine {
|
||||
if(it.isEmpty()) {
|
||||
builder.append(" *").append("\n")
|
||||
} else {
|
||||
builder.append(" * ").append(it).append("\n")
|
||||
}
|
||||
}
|
||||
|
||||
return builder.append(" */").append("\n").toString()
|
||||
}
|
||||
|
||||
property("license", generateLicenseHeader())
|
||||
property("modid", "cobblemon")
|
||||
property("version", project.version())
|
||||
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
|
||||
property("gitCommit", versioning.info.commit)
|
||||
property("branch", versioning.info.branch)
|
||||
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
|
||||
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails;
|
||||
import net.minecraft.util.crash.CrashReport;
|
||||
import net.minecraft.util.crash.CrashReportSection;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(CrashReport.class)
|
||||
public final class CrashReportMixin {
|
||||
|
||||
@Inject(method = "addStackTrace", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/SystemDetails;writeTo(Ljava/lang/StringBuilder;)V"))
|
||||
public void cobblemon$printCobblemonDetails(StringBuilder builder, CallbackInfo callback) {
|
||||
CrashReportSection cobblemon = new CrashReportSection("Cobblemon");
|
||||
cobblemon.add("Version", CobblemonBuildDetails.VERSION);
|
||||
cobblemon.add("Is Snapshot", CobblemonBuildDetails.SNAPSHOT);
|
||||
cobblemon.add("Git Commit", CobblemonBuildDetails.INSTANCE.smallCommitHash() + " (" + "https://gitlab.com/cable-mc/cobblemon/-/commit/" + CobblemonBuildDetails.GIT_COMMIT + ")");
|
||||
cobblemon.add("Branch", CobblemonBuildDetails.BRANCH);
|
||||
cobblemon.add("Timestamp", CobblemonBuildDetails.TIMESTAMP);
|
||||
|
||||
cobblemon.addStackTrace(builder);
|
||||
builder.append("\n\n");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import net.minecraft.entity.mob.PiglinBrain;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
|
||||
|
||||
|
||||
@Mixin(PiglinBrain.class)
|
||||
public class PiglinBarterMixin {
|
||||
|
||||
@Inject(method = "acceptsForBarter", at = @At(value = "RETURN"), cancellable = true)
|
||||
private static void cobblemon$acceptsForBarter(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
|
||||
if(!ci.getReturnValue()) {
|
||||
//TODO: Make a tag?
|
||||
if(stack.isOf(CobblemonItems.RELIC_COIN_POUCH))
|
||||
ci.setReturnValue(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems;
|
||||
import net.minecraft.entity.EquipmentSlot;
|
||||
import net.minecraft.entity.mob.PiglinEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(PiglinEntity.class)
|
||||
public abstract class PiglinEntityMixin {
|
||||
@Inject(method = "equipToOffHand", at = @At(value = "HEAD"), cancellable = true)
|
||||
public void cobblemon$isValidBarteringItem(ItemStack stack, CallbackInfo ci) {
|
||||
PiglinEntity entity = (PiglinEntity)(Object) this;
|
||||
if (stack.isOf(CobblemonItems.RELIC_COIN_POUCH)) {
|
||||
entity.equipStack(EquipmentSlot.OFFHAND, stack);
|
||||
entity.updateDropChances(EquipmentSlot.OFFHAND);
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{{ license }}
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
object CobblemonBuildDetails {
|
||||
|
||||
const val MOD_ID = "{{ modid }}"
|
||||
const val VERSION = "{{ version }}"
|
||||
const val SNAPSHOT = {{ isSnapshot }}
|
||||
|
||||
const val GIT_COMMIT = "{{ gitCommit | default("Not Specified") }}"
|
||||
const val BRANCH = "{{ branch | default("Not Specified") }}"
|
||||
const val TIMESTAMP = "{{ timestamp }}"
|
||||
|
||||
fun smallCommitHash(): String {
|
||||
return this.GIT_COMMIT.substring(0, 7)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails.smallCommitHash
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
@@ -16,6 +17,7 @@ import com.cobblemon.mod.common.api.data.DataProvider
|
||||
import com.cobblemon.mod.common.api.drop.CommandDropEntry
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.drop.ItemDropEntry
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
|
||||
@@ -36,6 +38,7 @@ import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
|
||||
import com.cobblemon.mod.common.api.pokemon.experience.StandardExperienceCalculator
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
|
||||
@@ -63,24 +66,14 @@ import com.cobblemon.mod.common.api.storage.player.PlayerDataStoreManager
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.JsonPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.storage.player.factory.MongoPlayerDataStoreFactory
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonEntityTypeTags
|
||||
import com.cobblemon.mod.common.battles.BagItems
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.ShowdownThread
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.battles.*
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.command.argument.DialogueArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.MoveArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
|
||||
import com.cobblemon.mod.common.command.argument.*
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.config.LastChangedVersion
|
||||
import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
@@ -101,13 +94,7 @@ import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
|
||||
import com.cobblemon.mod.common.sherds.CobblemonSherds
|
||||
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.trade.TradeManager
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.ifDedicatedServer
|
||||
import com.cobblemon.mod.common.util.isLaterVersion
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.removeAmountIf
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
|
||||
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
@@ -115,15 +102,6 @@ import com.mongodb.ConnectionString
|
||||
import com.mongodb.MongoClientSettings
|
||||
import com.mongodb.client.MongoClient
|
||||
import com.mongodb.client.MongoClients
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.io.PrintWriter
|
||||
import java.util.UUID
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.javaField
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer
|
||||
import net.minecraft.entity.data.TrackedDataHandlerRegistry
|
||||
@@ -133,12 +111,26 @@ import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.util.WorldSavePath
|
||||
import net.minecraft.world.World
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import org.apache.logging.log4j.Logger
|
||||
import java.io.File
|
||||
import java.io.FileReader
|
||||
import java.io.FileWriter
|
||||
import java.util.*
|
||||
import kotlin.collections.filterIsInstance
|
||||
import kotlin.collections.forEach
|
||||
import kotlin.collections.listOf
|
||||
import kotlin.collections.set
|
||||
import kotlin.math.roundToInt
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.reflect.full.memberProperties
|
||||
import kotlin.reflect.jvm.isAccessible
|
||||
import kotlin.reflect.jvm.javaField
|
||||
|
||||
object Cobblemon {
|
||||
const val MODID = "cobblemon"
|
||||
const val VERSION = "1.4.0"
|
||||
const val CONFIG_PATH = "config/$MODID/main.json"
|
||||
val LOGGER = LogManager.getLogger()
|
||||
val LOGGER: Logger = LogManager.getLogger()
|
||||
|
||||
lateinit var implementation: CobblemonImplementation
|
||||
@Deprecated("This field is now a config value", ReplaceWith("Cobblemon.config.captureCalculator"))
|
||||
@@ -160,7 +152,6 @@ object Cobblemon {
|
||||
var storage = PokemonStoreManager()
|
||||
var molangData = NbtMoLangDataStoreFactory
|
||||
lateinit var playerData: PlayerDataStoreManager
|
||||
lateinit var starterConfig: StarterConfig
|
||||
val dataProvider: DataProvider = CobblemonDataProvider
|
||||
var permissionValidator: PermissionValidator by Delegates.observable(LaxPermissionValidator().also { it.initialize() }) { _, _, newValue -> newValue.initialize() }
|
||||
var statProvider: StatProvider = CobblemonStatProvider
|
||||
@@ -168,6 +159,14 @@ object Cobblemon {
|
||||
|
||||
fun preInitialize(implementation: CobblemonImplementation) {
|
||||
this.implementation = implementation
|
||||
|
||||
this.LOGGER.info("Launching Cobblemon ${CobblemonBuildDetails.VERSION}${if(CobblemonBuildDetails.SNAPSHOT) "-SNAPSHOT" else ""} ")
|
||||
if(CobblemonBuildDetails.SNAPSHOT) {
|
||||
this.LOGGER.info(" - Git Commit: ${smallCommitHash()} (https://gitlab.com/cable-mc/cobblemon/-/commit/${CobblemonBuildDetails.GIT_COMMIT})")
|
||||
this.LOGGER.info(" - Branch: ${CobblemonBuildDetails.BRANCH}")
|
||||
this.LOGGER.info(" - Timestamp: ${CobblemonBuildDetails.TIMESTAMP}")
|
||||
}
|
||||
|
||||
implementation.registerPermissionValidator()
|
||||
implementation.registerSoundEvents()
|
||||
implementation.registerBlocks()
|
||||
@@ -230,6 +229,9 @@ object Cobblemon {
|
||||
}
|
||||
}
|
||||
|
||||
// Register the grow_tumblestone advancement
|
||||
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { AdvancementHandler.onTumbleStonePlaced(it) }
|
||||
|
||||
PlatformEvents.CHANGE_DIMENSION.subscribe {
|
||||
it.player.party().forEach { pokemon -> pokemon.entity?.recallWithAnimation() }
|
||||
}
|
||||
@@ -239,6 +241,16 @@ object Cobblemon {
|
||||
TrackedDataHandlerRegistry.register(PoseTypeDataSerializer)
|
||||
TrackedDataHandlerRegistry.register(IdentifierDataSerializer)
|
||||
|
||||
// Lowest priority because this applies after luxury ball bonus as of gen 4
|
||||
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(Priority.LOWEST) { event ->
|
||||
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
|
||||
// Our Luxury ball spec is diff from official, but we will still assume these stack
|
||||
if (event.pokemon.heldItemNoCopy().isIn(CobblemonItemTags.IS_FRIENDSHIP_BOOSTER)) {
|
||||
increment += increment * 0.5F
|
||||
}
|
||||
event.newFriendship = event.pokemon.friendship + increment.roundToInt()
|
||||
}
|
||||
|
||||
HeldItemProvider.register(CobblemonHeldItemManager, Priority.LOWEST)
|
||||
}
|
||||
|
||||
@@ -253,6 +265,7 @@ object Cobblemon {
|
||||
|
||||
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
|
||||
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
|
||||
|
||||
SpeciesFeatures.register(
|
||||
DataKeys.CAN_BE_MILKED,
|
||||
@@ -449,27 +462,6 @@ object Cobblemon {
|
||||
this.saveConfig()
|
||||
|
||||
bestSpawner.loadConfig()
|
||||
PokemonSpecies.observable.subscribe { starterConfig = this.loadStarterConfig() }
|
||||
}
|
||||
|
||||
fun loadStarterConfig(): StarterConfig {
|
||||
if (config.exportStarterConfig) {
|
||||
val file = File("config/cobblemon/starters.json")
|
||||
file.parentFile.mkdirs()
|
||||
if (!file.exists()) {
|
||||
val config = StarterConfig()
|
||||
val pw = PrintWriter(file)
|
||||
StarterConfig.GSON.toJson(config, pw)
|
||||
pw.close()
|
||||
return config
|
||||
}
|
||||
val reader = FileReader(file)
|
||||
val config = StarterConfig.GSON.fromJson(reader, StarterConfig::class.java)
|
||||
reader.close()
|
||||
return config
|
||||
} else {
|
||||
return StarterConfig()
|
||||
}
|
||||
}
|
||||
|
||||
fun saveConfig() {
|
||||
|
||||
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
|
||||
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import com.cobblemon.mod.common.block.entity.*
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.registry.Registries
|
||||
@@ -45,6 +46,18 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
@JvmField
|
||||
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.create(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.create(::GildedChestBlockEntity,
|
||||
CobblemonBlocks.GILDED_CHEST,
|
||||
CobblemonBlocks.BLUE_GILDED_CHEST,
|
||||
CobblemonBlocks.YELLOW_GILDED_CHEST,
|
||||
CobblemonBlocks.PINK_GILDED_CHEST,
|
||||
CobblemonBlocks.BLACK_GILDED_CHEST,
|
||||
CobblemonBlocks.WHITE_GILDED_CHEST,
|
||||
CobblemonBlocks.GREEN_GILDED_CHEST,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST
|
||||
).build(null))
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
|
||||
BlockEntityType.Builder.create({ pos, state -> FossilMultiblockEntity(pos, state, FossilMultiblockBuilder(pos)) },
|
||||
@@ -65,4 +78,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
CobblemonBlocks.FOSSIL_ANALYZER
|
||||
).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
|
||||
BlockEntityType.Builder.create(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.mixin.invoker.*
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
@@ -71,6 +72,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val SUN_STONE_ORE = evolutionStoneOre("sun_stone_ore")
|
||||
@JvmField
|
||||
val TERRACOTTA_SUN_STONE_ORE = evolutionStoneOre("terracotta_sun_stone_ore")
|
||||
@JvmField
|
||||
val THUNDER_STONE_ORE = evolutionStoneOre("thunder_stone_ore")
|
||||
@JvmField
|
||||
val WATER_STONE_ORE = evolutionStoneOre("water_stone_ore")
|
||||
@@ -151,13 +154,13 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val YELLOW_APRICORN_SAPLING = this.create("yellow_apricorn_sapling", ApricornSaplingBlock(PLANT_PROPERTIES, Apricorn.YELLOW))
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
|
||||
@JvmField
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
|
||||
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
|
||||
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.BIG_ROOT_SOUNDS)))
|
||||
@JvmField
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(CobblemonSounds.REVIVAL_HERB_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", GrowableStoneBlock.STAGE_3, 7, 3, null)
|
||||
@@ -191,6 +194,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
@@ -198,6 +202,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
@@ -205,6 +210,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
|
||||
@@ -223,6 +229,99 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
|
||||
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = create(
|
||||
"relic_coin_pouch",
|
||||
CoinPouchBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
|
||||
.pistonBehavior(PistonBehavior.DESTROY)
|
||||
.strength(0.4f)
|
||||
.nonOpaque(), true
|
||||
)
|
||||
)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = create(
|
||||
"relic_coin_sack",
|
||||
CoinPouchBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
|
||||
.pistonBehavior(PistonBehavior.DESTROY)
|
||||
.strength(0.4f), false
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST = create(
|
||||
"gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.RED
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BLUE_GILDED_CHEST = create(
|
||||
"blue_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.BLUE
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BLACK_GILDED_CHEST = create(
|
||||
"black_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.BLACK
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val YELLOW_GILDED_CHEST = create(
|
||||
"yellow_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.YELLOW
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val WHITE_GILDED_CHEST = create(
|
||||
"white_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.WHITE
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GREEN_GILDED_CHEST = create(
|
||||
"green_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.GREEN
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val PINK_GILDED_CHEST = create(
|
||||
"pink_gilded_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.PINK
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = create(
|
||||
"gimmighoul_chest",
|
||||
GildedChestBlock(
|
||||
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
|
||||
GildedChestBlock.Type.FAKE
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MONITOR = create(
|
||||
"monitor",
|
||||
@@ -255,7 +354,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
RestorationTankBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.IRON_GRAY)
|
||||
.sounds(BlockSoundGroup.METAL)
|
||||
.sounds(BlockSoundGroup.GLASS)
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.requiresTool()
|
||||
.strength(5.0F, 6.0F)
|
||||
@@ -290,17 +389,29 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val DISPLAY_CASE = create(
|
||||
"display_case",
|
||||
DisplayCaseBlock(
|
||||
AbstractBlock.Settings.create()
|
||||
.sounds(CobblemonSounds.DISPLAY_CASE_SOUNDS)
|
||||
.nonOpaque()
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.mapColor(MapColor.STONE_GRAY)
|
||||
.strength(0.3f)
|
||||
)
|
||||
)
|
||||
|
||||
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
@JvmField
|
||||
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create(
|
||||
@@ -317,7 +428,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE_SEEDS = this.create("vivichoke_seeds", VivichokeBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
|
||||
val VIVICHOKE_SEEDS = this.create("vivichoke_seeds", VivichokeBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.VIVICHOKE_SOUNDS)))
|
||||
@JvmField
|
||||
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(StatusEffects.LEVITATION, 10, AbstractBlock.Settings.create().mapColor(MapColor.DARK_GREEN).noCollision().breakInstantly().sounds(BlockSoundGroup.GRASS).offset(AbstractBlock.OffsetType.XZ).pistonBehavior(PistonBehavior.DESTROY)))
|
||||
@JvmField
|
||||
@@ -441,7 +552,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
|
||||
private fun berryBlock(name: String): BerryBlock {
|
||||
val identifier = cobblemonResource("${name}_berry")
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP).strength(0.2F)))
|
||||
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(CobblemonSounds.BERRY_BUSH_SOUNDS).strength(0.2F)))
|
||||
this.berries[identifier] = block
|
||||
return block
|
||||
}
|
||||
|
||||
@@ -48,5 +48,9 @@ object CobblemonCommands {
|
||||
PokemonRestartCommand.register(dispatcher)
|
||||
BedrockParticleCommand.register(dispatcher)
|
||||
OpenDialogueCommand.register(dispatcher)
|
||||
|
||||
// Possibly lock down registration if and only if under dev environment or running in an environment
|
||||
// with a certain system environment variable set
|
||||
CobblemonInfoCommand.register(dispatcher)
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
@@ -29,6 +30,8 @@ import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
|
||||
import com.cobblemon.mod.common.item.interactive.*
|
||||
import com.cobblemon.mod.common.item.interactive.PotionItem
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
|
||||
import com.cobblemon.mod.common.platform.PlatformRegistry
|
||||
import com.cobblemon.mod.common.pokeball.PokeBall
|
||||
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
|
||||
@@ -37,14 +40,7 @@ import net.minecraft.block.Block
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.effect.StatusEffectInstance
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
import net.minecraft.item.BlockItem
|
||||
import net.minecraft.item.FoodComponent
|
||||
import net.minecraft.item.HangingSignItem
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.SignItem
|
||||
import net.minecraft.item.SmithingTemplateItem
|
||||
import net.minecraft.item.StewItem
|
||||
import net.minecraft.item.*
|
||||
import net.minecraft.registry.Registries
|
||||
import net.minecraft.registry.Registry
|
||||
import net.minecraft.registry.RegistryKey
|
||||
@@ -229,6 +225,22 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val APRICORN_SIGN = this.create("apricorn_sign", SignItem(Item.Settings().maxCount(16), CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN))
|
||||
@JvmField
|
||||
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
|
||||
@JvmField
|
||||
val GILDED_CHEST = this.create("gilded_chest", BlockItem(CobblemonBlocks.GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val BLUE_GILDED_CHEST = this.create("blue_gilded_chest", BlockItem(CobblemonBlocks.BLUE_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val YELLOW_GILDED_CHEST = this.create("yellow_gilded_chest", BlockItem(CobblemonBlocks.YELLOW_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val PINK_GILDED_CHEST = this.create("pink_gilded_chest", BlockItem(CobblemonBlocks.PINK_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val BLACK_GILDED_CHEST = this.create("black_gilded_chest", BlockItem(CobblemonBlocks.BLACK_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val WHITE_GILDED_CHEST = this.create("white_gilded_chest", BlockItem(CobblemonBlocks.WHITE_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val GREEN_GILDED_CHEST = this.create("green_gilded_chest", BlockItem(CobblemonBlocks.GREEN_GILDED_CHEST, Item.Settings()))
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = this.create("gimmighoul_chest", BlockItem(CobblemonBlocks.GIMMIGHOUL_CHEST, Item.Settings()))
|
||||
|
||||
@JvmField
|
||||
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
|
||||
@@ -242,6 +254,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val PC = blockItem("pc", CobblemonBlocks.PC)
|
||||
@JvmField
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@@ -269,6 +283,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField val RIBBON_SWEET = noSettingsItem("ribbon_sweet")
|
||||
@JvmField val CHIPPED_POT = noSettingsItem("chipped_pot")
|
||||
@JvmField val CRACKED_POT = noSettingsItem("cracked_pot")
|
||||
@JvmField val MASTERPIECE_TEACUP = noSettingsItem("masterpiece_teacup")
|
||||
@JvmField val UNREMARKABLE_TEACUP = noSettingsItem("unremarkable_teacup")
|
||||
@JvmField val SWEET_APPLE = noSettingsItem("sweet_apple")
|
||||
@JvmField val TART_APPLE = noSettingsItem("tart_apple")
|
||||
@JvmField val GALARICA_CUFF = noSettingsItem("galarica_cuff")
|
||||
@@ -380,7 +396,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = create("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Settings().food(FoodComponent.Builder().snack().hunger(1).saturationModifier(0.2f).build())))
|
||||
val GENIUS_FEATHER = create("genius_feather", FeatherItem(Stats.SPECIAL_ATTACK))
|
||||
@JvmField
|
||||
val SWIFT_FEATHER = create("swift_feather", FeatherItem(Stats.SPEED))
|
||||
@JvmField
|
||||
val HEALTH_FEATHER = create("health_feather", FeatherItem(Stats.HP))
|
||||
@JvmField
|
||||
val RESIST_FEATHER = create("resist_feather", FeatherItem(Stats.DEFENCE))
|
||||
@JvmField
|
||||
val MUSCLE_FEATHER = create("muscle_feather", FeatherItem(Stats.ATTACK))
|
||||
@JvmField
|
||||
val CLEVER_FEATHER = create("clever_feather", FeatherItem(Stats.SPECIAL_DEFENCE))
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK = heldItem("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Settings().food(FoodComponent.Builder().snack().hunger(1).saturationModifier(0.2f).build())), "leek")
|
||||
@JvmField
|
||||
val ROASTED_LEEK = create("roasted_leek", Item(Item.Settings().food(FoodComponent.Builder().snack().hunger(3).saturationModifier(0.3f).build())))
|
||||
@JvmField
|
||||
@@ -530,6 +558,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false))
|
||||
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true))
|
||||
|
||||
@JvmField
|
||||
val ABILITY_CAPSULE = this.create("ability_capsule", AbilityChangeItem(AbilityChanger.COMMON_ABILITY))
|
||||
@JvmField
|
||||
val ABILITY_PATCH = this.create("ability_patch", AbilityChangeItem(AbilityChanger.HIDDEN_ABILITY))
|
||||
|
||||
/**
|
||||
* Evolution Ores and Stones
|
||||
*/
|
||||
@@ -550,6 +583,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val SUN_STONE_ORE = blockItem("sun_stone_ore", CobblemonBlocks.SUN_STONE_ORE)
|
||||
@JvmField
|
||||
val TERRACOTTA_SUN_STONE_ORE = blockItem("terracotta_sun_stone_ore", CobblemonBlocks.TERRACOTTA_SUN_STONE_ORE)
|
||||
@JvmField
|
||||
val THUNDER_STONE_ORE = blockItem("thunder_stone_ore", CobblemonBlocks.THUNDER_STONE_ORE)
|
||||
@JvmField
|
||||
val WATER_STONE_ORE = blockItem("water_stone_ore", CobblemonBlocks.WATER_STONE_ORE)
|
||||
@@ -601,16 +636,28 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
|
||||
// Held Items
|
||||
@JvmField
|
||||
val ABILITY_SHIELD = heldItem("ability_shield")
|
||||
@JvmField
|
||||
val ABSORB_BULB = heldItem("absorb_bulb")
|
||||
@JvmField
|
||||
val AIR_BALLOON = heldItem("air_balloon")
|
||||
@JvmField
|
||||
val ASSAULT_VEST = heldItem("assault_vest")
|
||||
@JvmField
|
||||
val BIG_ROOT = blockItem("big_root", CobblemonBlocks.BIG_ROOT)
|
||||
@JvmField
|
||||
val BINDING_BAND = heldItem("binding_band")
|
||||
@JvmField
|
||||
val BLACK_BELT = heldItem("black_belt")
|
||||
@JvmField
|
||||
val BLACK_GLASSES = heldItem("black_glasses")
|
||||
@JvmField
|
||||
val BLACK_SLUDGE = heldItem("black_sludge")
|
||||
@JvmField
|
||||
val BLUNDER_POLICY = heldItem("blunder_policy")
|
||||
@JvmField
|
||||
val CELL_BATTERY = heldItem("cell_battery")
|
||||
@JvmField
|
||||
val CHARCOAL = heldItem("charcoal_stick", remappedName = "charcoal")
|
||||
@JvmField
|
||||
val CHOICE_BAND = heldItem("choice_band")
|
||||
@@ -621,26 +668,64 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val CLEANSE_TAG = heldItem("cleanse_tag")
|
||||
@JvmField
|
||||
val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@JvmField
|
||||
val COVERT_CLOAK = heldItem("covert_cloak")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
@JvmField
|
||||
val DRAGON_FANG = heldItem("dragon_fang")
|
||||
@JvmField
|
||||
val EJECT_BUTTON = heldItem("eject_button")
|
||||
@JvmField
|
||||
val EJECT_PACK = heldItem("eject_pack")
|
||||
@JvmField
|
||||
val EVERSTONE = heldItem("everstone")
|
||||
@JvmField
|
||||
val EVIOLITE = heldItem("eviolite")
|
||||
@JvmField
|
||||
val EXP_SHARE = heldItem("exp_share")
|
||||
@JvmField
|
||||
val EXPERT_BELT = heldItem("expert_belt")
|
||||
@JvmField
|
||||
val FAIRY_FEATHER = heldItem("fairy_feather")
|
||||
@JvmField
|
||||
val FLAME_ORB = heldItem("flame_orb")
|
||||
@JvmField
|
||||
val FLOAT_STONE = heldItem("float_stone")
|
||||
@JvmField
|
||||
val FOCUS_BAND = heldItem("focus_band")
|
||||
@JvmField
|
||||
val FOCUS_SASH = heldItem("focus_sash")
|
||||
@JvmField
|
||||
val GRIP_CLAW = heldItem("grip_claw")
|
||||
@JvmField
|
||||
val HARD_STONE = heldItem("hard_stone")
|
||||
@JvmField
|
||||
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
|
||||
@JvmField
|
||||
val IRON_BALL = heldItem("iron_ball")
|
||||
@JvmField
|
||||
val LAGGING_TAIL = heldItem("lagging_tail")
|
||||
@JvmField
|
||||
val LEFTOVERS = heldItem("leftovers")
|
||||
@JvmField
|
||||
val LIFE_ORB = heldItem("life_orb")
|
||||
@JvmField
|
||||
val LIGHT_BALL = heldItem("light_ball")
|
||||
@JvmField
|
||||
val LIGHT_CLAY = heldItem("light_clay")
|
||||
@JvmField
|
||||
val LOADED_DICE = heldItem("loaded_dice")
|
||||
@JvmField
|
||||
val LUCKY_EGG = heldItem("lucky_egg")
|
||||
@JvmField
|
||||
val LUMINOUS_MOSS = heldItem("luminous_moss")
|
||||
@JvmField
|
||||
val MAGNET = heldItem("magnet")
|
||||
@JvmField
|
||||
val METRONOME = heldItem("metronome")
|
||||
@JvmField
|
||||
val MIRACLE_SEED = heldItem("miracle_seed")
|
||||
@JvmField
|
||||
val MUSCLE_BAND = heldItem("muscle_band")
|
||||
@@ -651,14 +736,42 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val POISON_BARB = heldItem("poison_barb")
|
||||
@JvmField
|
||||
val POWER_ANKLET = heldItem("power_anklet")
|
||||
@JvmField
|
||||
val POWER_BAND = heldItem("power_band")
|
||||
@JvmField
|
||||
val POWER_BELT = heldItem("power_belt")
|
||||
@JvmField
|
||||
val POWER_BRACER = heldItem("power_bracer")
|
||||
@JvmField
|
||||
val POWER_LENS = heldItem("power_lens")
|
||||
@JvmField
|
||||
val POWER_WEIGHT = heldItem("power_weight")
|
||||
@JvmField
|
||||
val PROTECTIVE_PADS = heldItem("protective_pads")
|
||||
@JvmField
|
||||
val PUNCHING_GLOVE = heldItem("punching_glove")
|
||||
@JvmField
|
||||
val QUICK_CLAW = heldItem("quick_claw")
|
||||
@JvmField
|
||||
val RED_CARD = heldItem("red_card")
|
||||
@JvmField
|
||||
val RING_TARGET = heldItem("ring_target")
|
||||
@JvmField
|
||||
val ROCKY_HELMET = heldItem("rocky_helmet")
|
||||
@JvmField
|
||||
val ROOM_SERVICE = heldItem("room_service")
|
||||
@JvmField
|
||||
val SAFETY_GOGGLES = heldItem("safety_goggles")
|
||||
@JvmField
|
||||
val SCOPE_LENS = heldItem("scope_lens")
|
||||
@JvmField
|
||||
val SHARP_BEAK = heldItem("sharp_beak")
|
||||
@JvmField
|
||||
val SHED_SHELL = heldItem("shed_shell")
|
||||
@JvmField
|
||||
val SHELL_BELL = heldItem("shell_bell")
|
||||
@JvmField
|
||||
val SILK_SCARF = heldItem("silk_scarf")
|
||||
@JvmField
|
||||
val SILVER_POWDER = heldItem("silver_powder")
|
||||
@@ -667,10 +780,30 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val SPELL_TAG = heldItem("spell_tag")
|
||||
@JvmField
|
||||
val SMOKE_BALL = heldItem("smoke_ball")
|
||||
@JvmField
|
||||
val SOOTHE_BELL = heldItem("soothe_bell")
|
||||
@JvmField
|
||||
val STICKY_BARB = heldItem("sticky_barb")
|
||||
@JvmField
|
||||
val TERRAIN_EXTENDER = heldItem("terrain_extender")
|
||||
@JvmField
|
||||
val THROAT_SPRAY = heldItem("throat_spray")
|
||||
@JvmField
|
||||
val TOXIC_ORB = heldItem("toxic_orb")
|
||||
@JvmField
|
||||
val TWISTED_SPOON = heldItem("twisted_spoon")
|
||||
@JvmField
|
||||
val UTILITY_UMBRELLA = heldItem("utility_umbrella")
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = heldItem("weakness_policy")
|
||||
@JvmField
|
||||
val WIDE_LENS = heldItem("wide_lens")
|
||||
@JvmField
|
||||
val WISE_GLASSES = heldItem("wise_glasses")
|
||||
@JvmField
|
||||
val ZOOM_LENS = heldItem("zoom_lens")
|
||||
@JvmField
|
||||
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 1F)
|
||||
@JvmField
|
||||
val MIRROR_HERB = compostableHeldItem("mirror_herb", null, 1F)
|
||||
@@ -685,29 +818,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val QUICK_POWDER = heldItem("quick_powder")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
val DAMP_ROCK = heldItem("damp_rock")
|
||||
@JvmField
|
||||
val EVERSTONE = heldItem("everstone")
|
||||
val HEAT_ROCK = heldItem("heat_rock")
|
||||
@JvmField
|
||||
val POWER_ANKLET = heldItem("power_anklet")
|
||||
val SMOOTH_ROCK = heldItem("smooth_rock")
|
||||
@JvmField
|
||||
val POWER_BAND = heldItem("power_band")
|
||||
@JvmField
|
||||
val POWER_BELT = heldItem("power_belt")
|
||||
@JvmField
|
||||
val POWER_BRACER = heldItem("power_bracer")
|
||||
@JvmField
|
||||
val POWER_LENS = heldItem("power_lens")
|
||||
@JvmField
|
||||
val POWER_WEIGHT = heldItem("power_weight")
|
||||
@JvmField
|
||||
val FLAME_ORB = heldItem("flame_orb")
|
||||
@JvmField
|
||||
val LIFE_ORB = heldItem("life_orb")
|
||||
@JvmField
|
||||
val TOXIC_ORB = heldItem("toxic_orb")
|
||||
@JvmField
|
||||
val SMOKE_BALL = heldItem("smoke_ball")
|
||||
val ICY_ROCK = heldItem("icy_rock")
|
||||
|
||||
// Mulch
|
||||
@JvmField
|
||||
@@ -733,19 +850,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val ARMOR_FOSSIL = noSettingsItem("armor_fossil")
|
||||
@JvmField
|
||||
val BIRD_FOSSIL = noSettingsItem("bird_fossil")
|
||||
val FOSSILIZED_BIRD = noSettingsItem("fossilized_bird")
|
||||
@JvmField
|
||||
val CLAW_FOSSIL = noSettingsItem("claw_fossil")
|
||||
@JvmField
|
||||
val COVER_FOSSIL = noSettingsItem("cover_fossil")
|
||||
@JvmField
|
||||
val DINO_FOSSIL = noSettingsItem("dino_fossil")
|
||||
val FOSSILIZED_DINO = noSettingsItem("fossilized_dino")
|
||||
@JvmField
|
||||
val DOME_FOSSIL = noSettingsItem("dome_fossil")
|
||||
@JvmField
|
||||
val DRAKE_FOSSIL = noSettingsItem("drake_fossil")
|
||||
val FOSSILIZED_DRAKE = noSettingsItem("fossilized_drake")
|
||||
@JvmField
|
||||
val FISH_FOSSIL = noSettingsItem("fish_fossil")
|
||||
val FOSSILIZED_FISH = noSettingsItem("fossilized_fish")
|
||||
@JvmField
|
||||
val HELIX_FOSSIL = noSettingsItem("helix_fossil")
|
||||
@JvmField
|
||||
@@ -824,6 +941,50 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
// Misc
|
||||
@JvmField
|
||||
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
|
||||
@JvmField
|
||||
val RELIC_COIN = noSettingsItem("relic_coin")
|
||||
@JvmField
|
||||
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
|
||||
@JvmField
|
||||
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
|
||||
|
||||
// Type Gems
|
||||
@JvmField
|
||||
val NORMAL_GEM = noSettingsItem("normal_gem")
|
||||
@JvmField
|
||||
val FIRE_GEM = noSettingsItem("fire_gem")
|
||||
@JvmField
|
||||
val WATER_GEM = noSettingsItem("water_gem")
|
||||
@JvmField
|
||||
val GRASS_GEM = noSettingsItem("grass_gem")
|
||||
@JvmField
|
||||
val ELECTRIC_GEM = noSettingsItem("electric_gem")
|
||||
@JvmField
|
||||
val ICE_GEM = noSettingsItem("ice_gem")
|
||||
@JvmField
|
||||
val FIGHTING_GEM = noSettingsItem("fighting_gem")
|
||||
@JvmField
|
||||
val POISON_GEM = noSettingsItem("poison_gem")
|
||||
@JvmField
|
||||
val GROUND_GEM = noSettingsItem("ground_gem")
|
||||
@JvmField
|
||||
val FLYING_GEM = noSettingsItem("flying_gem")
|
||||
@JvmField
|
||||
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
|
||||
@JvmField
|
||||
val BUG_GEM = noSettingsItem("bug_gem")
|
||||
@JvmField
|
||||
val ROCK_GEM = noSettingsItem("rock_gem")
|
||||
@JvmField
|
||||
val GHOST_GEM = noSettingsItem("ghost_gem")
|
||||
@JvmField
|
||||
val DRAGON_GEM = noSettingsItem("dragon_gem")
|
||||
@JvmField
|
||||
val DARK_GEM = noSettingsItem("dark_gem")
|
||||
@JvmField
|
||||
val STEEL_GEM = noSettingsItem("steel_gem")
|
||||
@JvmField
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
|
||||
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
|
||||
|
||||
@@ -850,6 +1011,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
}
|
||||
}
|
||||
)
|
||||
private fun heldItem(name: String, item: Item, remappedName: String? = null) = create(
|
||||
name = name,
|
||||
entry = item.also {
|
||||
if (remappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, remappedName)
|
||||
CobblemonHeldItemManager.registerRemap(Items.BONE, "thickclub")
|
||||
CobblemonHeldItemManager.registerRemap(Items.SNOWBALL, "snowball")
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
private fun compostable(item: Item, increaseLevelChance: Float) = Cobblemon.implementation.registerCompostable(item, increaseLevelChance)
|
||||
|
||||
|
||||
@@ -82,9 +82,12 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPa
|
||||
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
|
||||
@@ -248,6 +251,7 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
|
||||
// Evolution start
|
||||
@@ -314,6 +318,9 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
|
||||
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ import net.minecraft.registry.RegistryKey
|
||||
import net.minecraft.registry.RegistryKeys
|
||||
import net.minecraft.sound.BlockSoundGroup
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.sound.SoundEvents
|
||||
|
||||
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Registry<SoundEvent>>, SoundEvent>() {
|
||||
|
||||
@@ -84,6 +85,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
val MEDICINE_PILLS_USE = this.create("medicine_pills.use")
|
||||
@JvmField
|
||||
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
|
||||
@JvmField
|
||||
val MEDICINE_FEATHER_USE = this.create("medicine_feather.use")
|
||||
|
||||
@JvmField
|
||||
val BERRY_HARVEST = this.create("berry.harvest")
|
||||
@@ -96,28 +99,36 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
val MULCH_REMOVE = this.create("mulch.remove")
|
||||
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_ACTIVATE = this.create("fossilmachine.activate")
|
||||
val FOSSIL_MACHINE_ACTIVATE = this.create("fossil_machine.activate")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossilmachine.active_loop")
|
||||
val FOSSIL_MACHINE_ACTIVE_LOOP = this.create("fossil_machine.active_loop")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossilmachine.assemble")
|
||||
val FOSSIL_MACHINE_ASSEMBLE = this.create("fossil_machine.assemble")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_DNA_FULL = this.create("fossilmachine.dna_full")
|
||||
val FOSSIL_MACHINE_DNA_FULL = this.create("fossil_machine.dna_full")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_FINISHED = this.create("fossilmachine.finished")
|
||||
val FOSSIL_MACHINE_FINISHED = this.create("fossil_machine.finished")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossilmachine.insert_dna")
|
||||
val FOSSIL_MACHINE_INSERT_DNA = this.create("fossil_machine.insert_dna")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossilmachine.insert_dna_small")
|
||||
val FOSSIL_MACHINE_INSERT_DNA_SMALL = this.create("fossil_machine.insert_dna_small")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossilmachine.insert_fossil")
|
||||
val FOSSIL_MACHINE_INSERT_FOSSIL = this.create("fossil_machine.insert_fossil")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossilmachine.retrieve_fossil")
|
||||
val FOSSIL_MACHINE_RETRIEVE_FOSSIL = this.create("fossil_machine.retrieve_fossil")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossilmachine.retrieve_pokemon")
|
||||
val FOSSIL_MACHINE_RETRIEVE_POKEMON = this.create("fossil_machine.retrieve_pokemon")
|
||||
@JvmField
|
||||
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossilmachine.unprotected")
|
||||
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossil_machine.unprotected")
|
||||
|
||||
@JvmField
|
||||
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
|
||||
@JvmField
|
||||
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
|
||||
@JvmField
|
||||
val COIN_POUCH_STEP = this.create("coin_pouch.step")
|
||||
@JvmField
|
||||
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
|
||||
@JvmField
|
||||
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
|
||||
@JvmField
|
||||
@@ -129,6 +140,62 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
@JvmField
|
||||
val TUMBLESTONE_STEP = this.create("tumblestone.step")
|
||||
|
||||
@JvmField
|
||||
val GIMMIGHOUL_GIVE_ITEM_SMALL = this.create("gimmighoul.give_item_small")
|
||||
@JvmField
|
||||
val GIMMIGHOUL_REVEAL = this.create("gimmighoul.reveal")
|
||||
|
||||
@JvmField
|
||||
val BERRY_BUSH_BREAK = this.create("berry_bush.break")
|
||||
@JvmField
|
||||
val BERRY_BUSH_PLACE = this.create("berry_bush.place")
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_BREAK = this.create("big_root.break")
|
||||
@JvmField
|
||||
val ENERGY_ROOT_PLACE = this.create("energy_root.place")
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE_BREAK = this.create("vivichoke.break")
|
||||
@JvmField
|
||||
val VIVICHOKE_PLACE = this.create("vivichoke.place")
|
||||
|
||||
@JvmField
|
||||
val MINT_BREAK = this.create("mint.break")
|
||||
@JvmField
|
||||
val MINT_PLACE = this.create("mint.place")
|
||||
|
||||
@JvmField
|
||||
val REVIVAL_HERB_BREAK = this.create("revival_herb.break")
|
||||
@JvmField
|
||||
val REVIVAL_HERB_PLACE = this.create("revival_herb.place")
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_BREAK = this.create("medicinal_leek.break")
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_PLACE = this.create("medicinal_leek.plant")
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST_OPEN = this.create("gilded_chest.open")
|
||||
@JvmField
|
||||
val GILDED_CHEST_CLOSE = this.create("gilded_chest.close")
|
||||
@JvmField
|
||||
val GILDED_CHEST_STEP = this.create("gilded_chest.step")
|
||||
@JvmField
|
||||
val GILDED_CHEST_HIT = this.create("gilded_chest.hit")
|
||||
@JvmField
|
||||
val GILDED_CHEST_BREAK = this.create("gilded_chest.break")
|
||||
@JvmField
|
||||
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
|
||||
|
||||
@JvmField
|
||||
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
COIN_POUCH_BREAK,
|
||||
COIN_POUCH_STEP,
|
||||
COIN_POUCH_PLACE,
|
||||
COIN_POUCH_HIT,
|
||||
COIN_POUCH_STEP
|
||||
)
|
||||
@JvmField
|
||||
val IMPACT_NORMAL = this.create("impact.normal")
|
||||
@JvmField
|
||||
@@ -184,5 +251,99 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
|
||||
TUMBLESTONE_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BERRY_BUSH_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
BERRY_BUSH_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
BERRY_BUSH_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val BIG_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
BIG_ROOT_BREAK,
|
||||
SoundEvents.BLOCK_ROOTS_STEP,
|
||||
SoundEvents.BLOCK_ROOTS_PLACE,
|
||||
SoundEvents.BLOCK_ROOTS_HIT,
|
||||
SoundEvents.BLOCK_ROOTS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ENERGY_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
SoundEvents.BLOCK_ROOTS_BREAK,
|
||||
SoundEvents.BLOCK_ROOTS_STEP,
|
||||
ENERGY_ROOT_PLACE,
|
||||
SoundEvents.BLOCK_ROOTS_HIT,
|
||||
SoundEvents.BLOCK_ROOTS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MEDICINAL_LEEK_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
MEDICINAL_LEEK_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
MEDICINAL_LEEK_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val VIVICHOKE_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
VIVICHOKE_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
VIVICHOKE_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val MINT_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
MINT_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
MINT_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val REVIVAL_HERB_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
REVIVAL_HERB_BREAK,
|
||||
SoundEvents.BLOCK_GRASS_STEP,
|
||||
REVIVAL_HERB_PLACE,
|
||||
SoundEvents.BLOCK_GRASS_HIT,
|
||||
SoundEvents.BLOCK_GRASS_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val GILDED_CHEST_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
GILDED_CHEST_BREAK,
|
||||
GILDED_CHEST_STEP,
|
||||
GILDED_CHEST_PLACE,
|
||||
GILDED_CHEST_HIT,
|
||||
GILDED_CHEST_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE_ADD_ITEM = this.create("display_case.add_item")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_REMOVE_ITEM = this.create("display_case.remove_item")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_BREAK = this.create("display_case.break")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_HIT = this.create("display_case.hit")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_PLACE = this.create("display_case.place")
|
||||
@JvmField
|
||||
val DISPLAY_CASE_STEP = this.create("display_case.step")
|
||||
|
||||
@JvmField
|
||||
val DISPLAY_CASE_SOUNDS = BlockSoundGroup(1f, 1f,
|
||||
DISPLAY_CASE_BREAK,
|
||||
DISPLAY_CASE_STEP,
|
||||
DISPLAY_CASE_PLACE,
|
||||
DISPLAY_CASE_HIT,
|
||||
DISPLAY_CASE_STEP
|
||||
)
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
|
||||
}
|
||||
@@ -113,6 +113,14 @@ object CobblemonCriteria {
|
||||
)
|
||||
)
|
||||
|
||||
// Advancement criteria for [grow_tumblestone.json]
|
||||
val PLANT_TUMBLESTONE = this.create(
|
||||
SimpleCriterionTrigger(
|
||||
cobblemonResource("plant_tumblestone"),
|
||||
PlantTumblestoneCriterionCondition::class.java
|
||||
)
|
||||
)
|
||||
|
||||
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.advancement.criterion
|
||||
|
||||
import com.cobblemon.mod.common.block.TumblestoneBlock
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.predicate.entity.LootContextPredicate
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.BlockPos
|
||||
|
||||
/**
|
||||
* A criterion condition for when a tumblestone block is planted
|
||||
* @param id The identifier of the criterion
|
||||
* @param predicate The predicate for the criterion
|
||||
*
|
||||
* @author Aethen
|
||||
* @since 02/28/2024
|
||||
*/
|
||||
class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
|
||||
SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
|
||||
override fun toJson(json: JsonObject) {
|
||||
// Add properties to json if needed. None needed for this criterion
|
||||
}
|
||||
|
||||
override fun fromJson(json: JsonObject) {
|
||||
// Parse properties from json if needed. None needed for this criterion
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the tumblestone block can grow at the given position
|
||||
* @param player The player that planted the tumblestone block
|
||||
* @param context The context of the criterion
|
||||
* @return True if the tumblestone block can grow at the given position, false otherwise
|
||||
*/
|
||||
override fun matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
|
||||
return context.tumbleStoneBlock.canGrow(context.pos, player.world)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The context of the [PlantTumblestoneCriterionCondition]
|
||||
* @param pos The position of the tumblestone block
|
||||
* @param tumbleStoneBlock The tumblestone block
|
||||
*/
|
||||
open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.api.abilities
|
||||
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
@@ -21,7 +22,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @author Qu
|
||||
* @since January 9th, 2022
|
||||
*/
|
||||
open class Ability internal constructor(var template: AbilityTemplate, var forced: Boolean) {
|
||||
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean) {
|
||||
|
||||
val name: String
|
||||
get() = template.name
|
||||
@@ -34,16 +35,26 @@ open class Ability internal constructor(var template: AbilityTemplate, var force
|
||||
|
||||
/**
|
||||
* This represents the last known index of this backing ability in the species data.
|
||||
* If -1 the ability will attempt to find the correct index or reroll itself, this is done in order to be compatible with existing data
|
||||
* The value can be mutated however this is not recommended by API users.
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var forced: Boolean = forced
|
||||
internal set
|
||||
|
||||
/**
|
||||
* This represents the last known index of this backing ability in the species data.
|
||||
*
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var index: Int = -1
|
||||
internal set
|
||||
|
||||
/**
|
||||
* The last known priority of this ability in the species data.
|
||||
* The value can be mutated however this is not recommended by API users.
|
||||
*
|
||||
* @see [Pokemon.updateAbility].
|
||||
*/
|
||||
var priority = Priority.LOWEST
|
||||
internal set
|
||||
|
||||
open fun saveToNBT(nbt: NbtCompound): NbtCompound {
|
||||
nbt.putString(DataKeys.POKEMON_ABILITY_NAME, name)
|
||||
@@ -62,19 +73,23 @@ open class Ability internal constructor(var template: AbilityTemplate, var force
|
||||
}
|
||||
|
||||
open fun loadFromNBT(nbt: NbtCompound): Ability {
|
||||
forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
|
||||
this.template = Abilities.getOrException(nbt.getString(DataKeys.POKEMON_ABILITY_NAME))
|
||||
this.forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
|
||||
if (nbt.contains(DataKeys.POKEMON_ABILITY_INDEX) && nbt.contains(DataKeys.POKEMON_ABILITY_PRIORITY)) {
|
||||
this.index = nbt.getInt(DataKeys.POKEMON_ABILITY_INDEX)
|
||||
this.priority = Priority.valueOf(nbt.getString(DataKeys.POKEMON_ABILITY_PRIORITY))
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
open fun loadFromJSON(json: JsonObject): Ability {
|
||||
forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
|
||||
this.template = Abilities.getOrException(json.get(DataKeys.POKEMON_ABILITY_NAME).asString)
|
||||
this.forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
|
||||
if (json.has(DataKeys.POKEMON_ABILITY_INDEX) && json.has(DataKeys.POKEMON_ABILITY_PRIORITY)) {
|
||||
this.index = json.get(DataKeys.POKEMON_ABILITY_INDEX).asInt
|
||||
this.priority = Priority.valueOf(json.get(DataKeys.POKEMON_ABILITY_PRIORITY).asString)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,7 +28,6 @@ class AbilityTemplate(
|
||||
*/
|
||||
fun create(forced: Boolean = false) = builder(this, forced)
|
||||
|
||||
|
||||
/**
|
||||
* Returns the Ability and loads the given NBT Tag into it.
|
||||
*
|
||||
@@ -42,4 +41,5 @@ class AbilityTemplate(
|
||||
* Ability extensions need to write and read their needed data from here.
|
||||
*/
|
||||
fun create(json: JsonObject) = create().loadFromJSON(json)
|
||||
|
||||
}
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.drop
|
||||
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
|
||||
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -88,6 +89,8 @@ class DropTable {
|
||||
amount: IntRange = this.amount
|
||||
) {
|
||||
val drops = getDrops(amount).toMutableList()
|
||||
val heldItem = (entity as PokemonEntity).pokemon.heldItemNoCopy()
|
||||
if (!heldItem.isEmpty) entity.dropItem(heldItem.item)
|
||||
LOOT_DROPPED.postThen(
|
||||
event = LootDroppedEvent(this, player, entity, drops),
|
||||
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
|
||||
|
||||
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEv
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.HeldItemUpdatedEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
|
||||
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
|
||||
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
|
||||
@@ -66,6 +67,8 @@ object CobblemonEvents {
|
||||
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
|
||||
@JvmField
|
||||
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
|
||||
@JvmField
|
||||
val HELD_ITEM_UPDATED = CancelableObservable<HeldItemUpdatedEvent>()
|
||||
|
||||
@JvmField
|
||||
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon.interaction
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
/**
|
||||
* Event published when a held item is being updated on a [Pokemon].
|
||||
* Cancelling the event will prevent any action from being taken on
|
||||
* the Pokémon.
|
||||
*
|
||||
* [newItem] differs from [originalStack] in that the former is a
|
||||
* one-sized version of the original (Pokémon cannot hold stacks) and
|
||||
* changing it in any way will not affect the original item held by
|
||||
* the player.
|
||||
*
|
||||
* @author Whatsy, Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
data class HeldItemUpdatedEvent(
|
||||
val cause: LivingEntity?,
|
||||
val pokemon: Pokemon,
|
||||
val originalStack: ItemStack,
|
||||
val decrement: Boolean,
|
||||
val oldItem: ItemStack,
|
||||
val newItem: ItemStack
|
||||
) : Cancelable()
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.item.ability
|
||||
|
||||
import com.cobblemon.mod.common.api.abilities.*
|
||||
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
|
||||
|
||||
/**
|
||||
* Represents a change operation for a [Pokemon.ability].
|
||||
* This is backed by the associated [PotentialAbility].
|
||||
*
|
||||
* @param T The type of [PotentialAbility].
|
||||
*/
|
||||
interface AbilityChanger<T : PotentialAbility> {
|
||||
|
||||
/**
|
||||
* The [PotentialAbilityType] of type [T].
|
||||
*/
|
||||
val type: PotentialAbilityType<T>
|
||||
|
||||
/**
|
||||
* Collects all the possible targets for the [type] and the given [pokemon].
|
||||
*
|
||||
* @param pokemon The [Pokemon] being queried.
|
||||
* @return A set containing all their legal abilities associated with [type], can be empty.
|
||||
*/
|
||||
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
|
||||
|
||||
/**
|
||||
* Attempts to update the ability of the given [pokemon].
|
||||
*
|
||||
* @param pokemon The [Pokemon] attempting to change an ability.
|
||||
* @return If the operation was successful.
|
||||
*/
|
||||
fun performChange(pokemon: Pokemon): Boolean
|
||||
|
||||
/**
|
||||
* Checks if the current [PotentialAbilityType] of a [Pokemon.ability] is possible to swap from with this.
|
||||
*
|
||||
* @param type The [PotentialAbilityType] or null if the ability is forced and/or illegal.
|
||||
* @return If the operation can be performed.
|
||||
*/
|
||||
fun canChangeFrom(type: PotentialAbilityType<*>?): Boolean
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* An implementation of [AbilityChanger] for [CommonAbility].
|
||||
*
|
||||
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 2 common abilities.
|
||||
*/
|
||||
@JvmStatic
|
||||
val COMMON_ABILITY: AbilityChanger<CommonAbility> = AbilityTypeChanger(CommonAbilityType) { other -> other == CommonAbilityType }
|
||||
|
||||
/**
|
||||
* An implementation of [AbilityChanger] for [HiddenAbility].
|
||||
*
|
||||
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 1 hidden ability.
|
||||
*/
|
||||
@JvmStatic
|
||||
val HIDDEN_ABILITY: AbilityChanger<HiddenAbility> = AbilityTypeChanger(HiddenAbilityType) { other -> other == CommonAbilityType || other == HiddenAbilityType }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
|
||||
/**
|
||||
@@ -34,8 +35,8 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
|
||||
itemStack: ItemStack?
|
||||
) {
|
||||
if (world is ServerWorld) {
|
||||
val multiblockEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
multiblockEntity.multiblockBuilder?.validate(world)
|
||||
val multiblockEntity = world.getBlockEntity(pos) as? MultiblockEntity
|
||||
multiblockEntity?.multiblockBuilder?.validate(world)
|
||||
}
|
||||
super.onPlaced(world, pos, state, placer, itemStack)
|
||||
}
|
||||
@@ -51,8 +52,8 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
|
||||
if(hand == Hand.OFF_HAND) {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
val entity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
if (entity.multiblockStructure != null) {
|
||||
val entity = world.getBlockEntity(pos) as MultiblockEntity?
|
||||
if (entity?.multiblockStructure != null) {
|
||||
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
@@ -77,6 +78,11 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
|
||||
return BlockRenderType.MODEL
|
||||
}
|
||||
|
||||
override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack {
|
||||
val blockEntity = world.getBlockEntity(pos) as? MultiblockEntity ?: return ItemStack.EMPTY
|
||||
return if (blockEntity.multiblockStructure == null) super.getPickStack(world, pos, state) else ItemStack.EMPTY
|
||||
}
|
||||
|
||||
abstract fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity
|
||||
|
||||
}
|
||||
|
||||
@@ -205,8 +205,8 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("ancient_wing_ball", throwPower = 2.5f, ancient = true)
|
||||
createDefault("ancient_jet_ball", throwPower = 2.5f, ancient = true)
|
||||
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
|
||||
// Luxury ball effect
|
||||
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOWEST) { event ->
|
||||
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
|
||||
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
|
||||
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
|
||||
if (increment <= 1F) {
|
||||
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
|
||||
|
||||
@@ -13,12 +13,14 @@ import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.abilities.Ability
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraTypes
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.pokemon.*
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
@@ -294,6 +296,18 @@ open class PokemonProperties {
|
||||
)
|
||||
|
||||
fun apply(pokemon: Pokemon) {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
this.customProperties.forEach { it.apply(pokemon) }
|
||||
this.commonApply(pokemon)
|
||||
}
|
||||
|
||||
fun apply(pokemonEntity: PokemonEntity) {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
this.customProperties.forEach { it.apply(pokemonEntity) }
|
||||
this.commonApply(pokemonEntity.pokemon)
|
||||
}
|
||||
|
||||
private fun commonApply(pokemon: Pokemon) {
|
||||
species?.let {
|
||||
return@let try {
|
||||
if (it == "random") {
|
||||
@@ -313,8 +327,8 @@ open class PokemonProperties {
|
||||
friendship?.let { pokemon.setFriendship(it) }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
|
||||
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemon.ability = ability } }
|
||||
status?.let { Statuses.getStatus(it)?.let { pokemon.applyStatus(it as PersistentStatus) } }
|
||||
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
|
||||
status?.let { Statuses.getStatus(it)?.let { status -> if (status is PersistentStatus) pokemon.applyStatus(status) } }
|
||||
customProperties.forEach { it.apply(pokemon) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
@@ -326,7 +340,7 @@ open class PokemonProperties {
|
||||
pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
|
||||
teraType?.let { TeraTypes.get(it.asIdentifierDefaultingNamespace())?.let { type -> pokemon.teraType = type } }
|
||||
dmaxLevel?.let { pokemon.dmaxLevel = it }
|
||||
gmaxFactor?.let { pokemon.gmaxFactor = it }
|
||||
tradeable?.let { pokemon.tradeable = it }
|
||||
@@ -355,69 +369,17 @@ open class PokemonProperties {
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
|
||||
fun apply(pokemonEntity: PokemonEntity) {
|
||||
species?.let {
|
||||
return@let try {
|
||||
if (it == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace())
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {
|
||||
null
|
||||
}
|
||||
}?.let { pokemonEntity.pokemon.species = it }
|
||||
nickname?.let { pokemonEntity.pokemon.nickname = it }
|
||||
form?.let { formID -> pokemonEntity.pokemon.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }?.let { form -> pokemonEntity.pokemon.form = form }
|
||||
level?.let { pokemonEntity.pokemon.level = it }
|
||||
shiny?.let { pokemonEntity.pokemon.shiny = it }
|
||||
gender?.let { pokemonEntity.pokemon.gender = it }
|
||||
friendship?.let { pokemonEntity.pokemon.setFriendship(it) }
|
||||
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemonEntity.pokemon.caughtBall = pokeball } }
|
||||
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemonEntity.pokemon.nature = nature } }
|
||||
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemonEntity.pokemon.ability = ability } }
|
||||
status?.let { Statuses.getStatus(it)?.let { pokemonEntity.pokemon.applyStatus(it as PersistentStatus) } }
|
||||
customProperties.forEach { it.apply(pokemonEntity) }
|
||||
ivs?.let { ivs ->
|
||||
ivs.forEach { stat ->
|
||||
pokemonEntity.pokemon.setIV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
evs?.let { evs ->
|
||||
evs.forEach { stat ->
|
||||
pokemonEntity.pokemon.setEV(stat.key, stat.value)
|
||||
}
|
||||
}
|
||||
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
|
||||
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
|
||||
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
|
||||
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
|
||||
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
|
||||
originalTrainerType?.let {
|
||||
if (it == OriginalTrainerType.NONE) {
|
||||
pokemonEntity.pokemon.removeOriginalTrainer()
|
||||
originalTrainer = null
|
||||
}
|
||||
}
|
||||
originalTrainer?.let { ot ->
|
||||
val type = originalTrainerType ?: pokemonEntity.pokemon.originalTrainerType
|
||||
when (type) {
|
||||
OriginalTrainerType.PLAYER -> {
|
||||
when (ot.length) {
|
||||
in 3..16 -> server()?.userCache?.findByName(ot)?.get()?.id // OT is a Username
|
||||
36 -> UUID.fromString(ot) // OT is a UUID
|
||||
else -> null // OT is invalid
|
||||
}?.let { uuid -> pokemonEntity.pokemon.setOriginalTrainer(uuid) }
|
||||
}
|
||||
OriginalTrainerType.NPC -> pokemonEntity.pokemon.setOriginalTrainer(ot)
|
||||
else -> {}
|
||||
}
|
||||
pokemonEntity.pokemon.refreshOriginalTrainer()
|
||||
}
|
||||
pokemonEntity.pokemon.updateAspects()
|
||||
fun matches(pokemon: Pokemon): Boolean {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
return this.commonMatches(pokemon) && this.customProperties.none { !it.matches(pokemon) }
|
||||
}
|
||||
|
||||
fun matches(pokemon: Pokemon): Boolean {
|
||||
fun matches(pokemonEntity: PokemonEntity): Boolean {
|
||||
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
|
||||
return this.commonMatches(pokemonEntity.pokemon) && this.customProperties.none { !it.matches(pokemonEntity) }
|
||||
}
|
||||
|
||||
private fun commonMatches(pokemon: Pokemon): Boolean {
|
||||
level?.takeIf { it != pokemon.level }?.let { return false }
|
||||
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
|
||||
gender?.takeIf { it != pokemon.gender }?.let { return false }
|
||||
@@ -435,7 +397,7 @@ open class PokemonProperties {
|
||||
return false
|
||||
}
|
||||
}
|
||||
nickname?.takeIf { it != pokemon.nickname }?.let { return false }
|
||||
nickname?.takeIf { it.string != pokemon.nickname?.string }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemon.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
@@ -448,59 +410,23 @@ open class PokemonProperties {
|
||||
evs?.forEach{ stat ->
|
||||
if (stat.value != pokemon.evs[stat.key]) { return false }
|
||||
}
|
||||
teraType?.takeIf { it != pokemon.teraType.name }?.let { return false }
|
||||
teraType?.takeIf { it.asIdentifierDefaultingNamespace() != pokemon.teraType.id }?.let { return false }
|
||||
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
|
||||
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
|
||||
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
|
||||
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let{ return false }
|
||||
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let{ return false }
|
||||
return customProperties.none { !it.matches(pokemon) }
|
||||
}
|
||||
|
||||
fun matches(pokemonEntity: PokemonEntity): Boolean {
|
||||
level?.takeIf { it != pokemonEntity.pokemon.level }?.let { return false }
|
||||
shiny?.takeIf { it != ("shiny" in pokemonEntity.pokemon.aspects) }?.let { return false }
|
||||
gender?.takeIf { it != pokemonEntity.pokemon.gender }?.let { return false }
|
||||
species?.run {
|
||||
try {
|
||||
val species = if (this == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace()) ?: return@run
|
||||
}
|
||||
if (pokemonEntity.pokemon.species != species) {
|
||||
return false
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {}
|
||||
}
|
||||
nickname?.takeIf { it != pokemonEntity.pokemon.nickname }?.let { return false }
|
||||
form?.takeIf { !it.equals(pokemonEntity.pokemon.form.name, true) }?.let { return false }
|
||||
friendship?.takeIf { it != pokemonEntity.pokemon.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != pokemonEntity.pokemon.caughtBall.name.toString() }?.let { return false }
|
||||
nature?.takeIf { it != pokemonEntity.pokemon.nature.name.toString() }?.let { return false }
|
||||
ability?.takeIf { it != pokemonEntity.pokemon.ability.name }?.let { return false }
|
||||
status?.takeIf { it != pokemonEntity.pokemon.status?.status?.showdownName }?.let { return false }
|
||||
ivs?.forEach{ stat ->
|
||||
if (stat.value != pokemonEntity.pokemon.ivs[stat.key]) { return false }
|
||||
}
|
||||
evs?.forEach{ stat ->
|
||||
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
|
||||
}
|
||||
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
|
||||
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
|
||||
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
|
||||
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
|
||||
originalTrainer?.takeIf { it != pokemonEntity.pokemon.originalTrainer }?.let{ return false }
|
||||
originalTrainerType?.takeIf { it != pokemonEntity.pokemon.originalTrainerType }?.let{ return false }
|
||||
return customProperties.none { !it.matches(pokemonEntity) }
|
||||
return true
|
||||
}
|
||||
|
||||
// TODO Review the need for this, usage for us is in commands that deviate from the behavior established by multiple Pokémon query equivalent
|
||||
fun isSubSetOf(properties: PokemonProperties): Boolean {
|
||||
level?.takeIf { it != properties.level }?.let { return false }
|
||||
shiny?.takeIf { it != ("shiny" in properties.aspects) }?.let { return false }
|
||||
gender?.takeIf { it != properties.gender }?.let { return false }
|
||||
species?.run {
|
||||
try {
|
||||
// TODO get context on this bit, it's highly unlikely two randoms would result in the same Pokémon, doesn't mean the prop is not equal?
|
||||
val species = if (this == "random") {
|
||||
PokemonSpecies.species.random()
|
||||
} else {
|
||||
@@ -509,9 +435,9 @@ open class PokemonProperties {
|
||||
if (properties.species != species.toString()) {
|
||||
return false
|
||||
}
|
||||
} catch (e: InvalidIdentifierException) {}
|
||||
} catch (_: InvalidIdentifierException) {}
|
||||
}
|
||||
nickname?.takeIf { it != properties.nickname }?.let { return false }
|
||||
nickname?.takeIf { it.string != properties.nickname?.string }?.let { return false }
|
||||
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
|
||||
friendship?.takeIf { it != properties.friendship }?.let { return false }
|
||||
pokeball?.takeIf { it != properties.pokeball }?.let { return false }
|
||||
@@ -551,20 +477,18 @@ open class PokemonProperties {
|
||||
|
||||
// TEST YOUR LUCK!
|
||||
fun roll(pokemon: Pokemon) {
|
||||
val baseTypes = pokemon.species.types.toList()
|
||||
val baseTypes = pokemon.form.types.toList()
|
||||
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
|
||||
if (this.teraType == null) pokemon.teraType =
|
||||
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
|
||||
else baseTypes.random()
|
||||
if (Cobblemon.config.teraTypeRate.checkRate()) TeraTypes.random(true)
|
||||
else TeraTypes.forElementalType(baseTypes.random())
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
|
||||
|
||||
fun createEntity(world: World): PokemonEntity {
|
||||
return PokemonEntity(world, create())
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun saveToNBT(): NbtCompound {
|
||||
val nbt = NbtCompound()
|
||||
originalString.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
|
||||
@@ -593,6 +517,7 @@ open class PokemonProperties {
|
||||
return nbt
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun loadFromNBT(tag: NbtCompound): PokemonProperties {
|
||||
originalString = tag.getString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)
|
||||
level = if (tag.contains(DataKeys.POKEMON_LEVEL)) tag.getInt(DataKeys.POKEMON_LEVEL) else null
|
||||
@@ -621,6 +546,7 @@ open class PokemonProperties {
|
||||
return this
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun saveToJSON(): JsonObject {
|
||||
val json = JsonObject()
|
||||
originalString.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
|
||||
@@ -650,6 +576,7 @@ open class PokemonProperties {
|
||||
return json
|
||||
}
|
||||
|
||||
// TODO Codecs at some point
|
||||
fun loadFromJSON(json: JsonObject): PokemonProperties {
|
||||
originalString = json.get(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)?.asString ?: ""
|
||||
level = json.get(DataKeys.POKEMON_LEVEL)?.asInt
|
||||
@@ -727,4 +654,21 @@ open class PokemonProperties {
|
||||
fun copy(): PokemonProperties {
|
||||
return PokemonProperties().loadFromJSON(saveToJSON())
|
||||
}
|
||||
|
||||
// If the config value is at least 1, then do 1/x and use that as the property chance
|
||||
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
|
||||
|
||||
/**
|
||||
* Attempts to find an ability by ID and resolve if it should be forced or if it's legal for the given form.
|
||||
*
|
||||
* @param id The literal ID of the [Ability] being mapped.
|
||||
* @param form The [FormData] whose [FormData.abilities] is queried for legality.
|
||||
* @return The [Ability] with the [Ability.forced] state necessary for the given [form].
|
||||
*/
|
||||
private fun createAbility(id: String, form: FormData): Ability? {
|
||||
val ability = Abilities.get(id) ?: return null
|
||||
val potentialAbility = form.abilities.firstOrNull { potential -> potential.template == ability } ?: return ability.create(true)
|
||||
return potentialAbility.template.create(false)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+6
@@ -34,4 +34,10 @@ interface PassiveEvolution : Evolution {
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* If the evolution will disappear once the conditions are no longer met.
|
||||
*/
|
||||
val permanent: Boolean
|
||||
|
||||
|
||||
}
|
||||
+39
-6
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common.api.pokemon.feature
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.substitute
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A [SpeciesFeatureProvider] which is a string value selected from a fixed list of choices. Parameters exist
|
||||
@@ -24,15 +26,46 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @since November 30th, 2022
|
||||
*/
|
||||
open class ChoiceSpeciesFeatureProvider(
|
||||
override val keys: List<String>,
|
||||
override var keys: List<String>,
|
||||
var default: String? = null,
|
||||
var choices: List<String> = listOf(),
|
||||
var isAspect: Boolean = true,
|
||||
var aspectFormat: String = "{{choice}}"
|
||||
) : SpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
|
||||
override val needsKey = true
|
||||
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
|
||||
override var needsKey = true
|
||||
override var visible = false
|
||||
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeCollection(choices) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeBoolean(isAspect)
|
||||
buffer.writeString(aspectFormat)
|
||||
buffer.writeBoolean(needsKey)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
keys = buffer.readList { buffer.readString() }
|
||||
default = buffer.readNullable { buffer.readString() }
|
||||
choices = buffer.readList { buffer.readString() }
|
||||
isAspect = buffer.readBoolean()
|
||||
aspectFormat = buffer.readString()
|
||||
needsKey = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<StringSpeciesFeature>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun invoke(buffer: PacketByteBuf, name: String): StringSpeciesFeature? {
|
||||
return if (name in keys) {
|
||||
StringSpeciesFeature(name, "").also { it.decode(buffer) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
fun getAllAspects(): MutableList<String> {
|
||||
val aspects = choices.toMutableList()
|
||||
choices.forEach {
|
||||
@@ -45,10 +78,10 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
internal constructor(): this(emptyList())
|
||||
|
||||
fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
override fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
|
||||
val existing = pokemon.getFeature<StringSpeciesFeature>(keys.first())
|
||||
val existing = get(pokemon)
|
||||
return if (existing != null && existing.value in choices) {
|
||||
existing
|
||||
} else {
|
||||
@@ -88,7 +121,7 @@ open class ChoiceSpeciesFeatureProvider(
|
||||
|
||||
override fun provide(pokemon: Pokemon): Set<String> {
|
||||
return if (isAspect) {
|
||||
pokemon.getFeature<StringSpeciesFeature>(keys.first())?.let { setOf(getAspect(it)) } ?: emptySet()
|
||||
get(pokemon)?.let { setOf(getAspect(it)) } ?: emptySet()
|
||||
} else {
|
||||
emptySet()
|
||||
}
|
||||
|
||||
+43
-5
@@ -12,10 +12,12 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A simple [SpeciesFeature] that is a true/false flag value. It implements [CustomPokemonProperty] for use in
|
||||
@@ -26,7 +28,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
* @author Hiroku
|
||||
* @since May 13th, 2022
|
||||
*/
|
||||
open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, CustomPokemonProperty {
|
||||
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
constructor(name: String, enabled: Boolean): this(name) {
|
||||
this.enabled = enabled
|
||||
}
|
||||
@@ -53,6 +55,14 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
|
||||
return this
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeBoolean(enabled)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
enabled = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$enabled"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
@@ -71,11 +81,37 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
|
||||
}
|
||||
|
||||
class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
|
||||
override val keys: List<String>
|
||||
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
|
||||
override var keys: List<String> = emptyList()
|
||||
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
|
||||
override val needsKey get() = true
|
||||
var default: String? = null
|
||||
val isAspect = true
|
||||
var isAspect = true
|
||||
override var visible: Boolean = false
|
||||
|
||||
override fun invoke(buffer: PacketByteBuf, name: String): FlagSpeciesFeature? {
|
||||
return if (name in keys) {
|
||||
FlagSpeciesFeature(name).also { it.decode(buffer) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeBoolean(isAspect)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
keys = buffer.readList { it.readString() }
|
||||
default = buffer.readNullable { it.readString() }
|
||||
isAspect = buffer.readBoolean()
|
||||
}
|
||||
|
||||
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<FlagSpeciesFeature>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun examples() = setOf("true", "false")
|
||||
|
||||
@@ -94,8 +130,10 @@ class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, C
|
||||
|
||||
constructor(vararg keys: String) : this(keys.toList())
|
||||
|
||||
override fun get(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(keys.first())
|
||||
|
||||
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
|
||||
return pokemon.getFeature(keys.first())
|
||||
return get(pokemon)
|
||||
?: when (default) {
|
||||
"random" -> FlagSpeciesFeature(keys.first(), Random.Default.nextBoolean())
|
||||
in setOf("true", "false") -> FlagSpeciesFeature(keys.first(), default.toBoolean())
|
||||
|
||||
+14
-1
@@ -8,13 +8,17 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -24,6 +28,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* A registry very similar to [SpeciesFeatures] but the [SpeciesFeatureProvider] contained within it
|
||||
@@ -42,11 +47,15 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
|
||||
override val resourcePath: String = "global_species_features"
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
player.sendPacket(GlobalSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
|
||||
}
|
||||
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
|
||||
resourceFeatures.keys.toList().forEach(this::unregister)
|
||||
@@ -59,6 +68,10 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
|
||||
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
|
||||
|
||||
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
|
||||
codeFeatures.putAll(entries.map { it.toPair() })
|
||||
}
|
||||
|
||||
private fun register(name: String, provider: SpeciesFeatureProvider<*>, isCoded: Boolean) {
|
||||
val mapping = if (isCoded) codeFeatures else resourceFeatures
|
||||
if (provider is AspectProvider) {
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.JsonObject
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* A species feature value that's just an integer. Complex stuff.
|
||||
* @author Hiroku
|
||||
*/
|
||||
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
var value = 0
|
||||
|
||||
constructor(): this("")
|
||||
constructor(name: String, value: Int): this(name) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putInt(name, value)
|
||||
return pokemonNBT
|
||||
}
|
||||
|
||||
override fun loadFromNBT(pokemonNBT: NbtCompound): SynchronizedSpeciesFeature {
|
||||
value = pokemonNBT.getInt(name)
|
||||
return this
|
||||
}
|
||||
|
||||
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
|
||||
pokemonJSON.addProperty(name, value)
|
||||
return pokemonJSON
|
||||
}
|
||||
|
||||
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
|
||||
value = pokemonJSON.get(name).asInt
|
||||
return this
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeInt(value)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
value = buffer.readInt()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$value"
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
|
||||
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
|
||||
val existingFeature = pokemon.getFeature<IntSpeciesFeature>(name)
|
||||
if (existingFeature != null) {
|
||||
existingFeature.value = value
|
||||
} else {
|
||||
pokemon.features.add(IntSpeciesFeature(name, value))
|
||||
}
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
|
||||
override fun matches(pokemon: Pokemon) = pokemon.getFeature<IntSpeciesFeature>(name)?.value == value
|
||||
}
|
||||
|
||||
class IntSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<IntSpeciesFeature>, CustomPokemonPropertyType<IntSpeciesFeature> {
|
||||
class DisplayData : Encodable, Decodable {
|
||||
var name: String = ""
|
||||
@SerializedName(value = "colour" /* fuck you we use real english */, alternate = ["color"])
|
||||
var colour = Vec3d(255.0, 255.0, 255.0)
|
||||
var underlay: Identifier? = null
|
||||
var overlay: Identifier? = null
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
name = buffer.readString()
|
||||
colour = Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble())
|
||||
underlay = buffer.readNullable { buffer.readIdentifier() }
|
||||
overlay = buffer.readNullable { buffer.readIdentifier() }
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(name)
|
||||
buffer.writeDouble(colour.x)
|
||||
buffer.writeDouble(colour.y)
|
||||
buffer.writeDouble(colour.z)
|
||||
buffer.writeNullable(underlay) { _, value -> buffer.writeIdentifier(value) }
|
||||
buffer.writeNullable(overlay) { _, value -> buffer.writeIdentifier(value) }
|
||||
}
|
||||
}
|
||||
|
||||
override var keys = listOf<String>()
|
||||
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
|
||||
override val needsKey get() = true
|
||||
override var visible = false
|
||||
var default: Int? = null
|
||||
var min = 0
|
||||
var max = 100
|
||||
var display: DisplayData? = null
|
||||
|
||||
override fun fromString(value: String?) = value?.toIntOrNull()?.takeIf { it in min..max }?.let { IntSpeciesFeature(keys.first(), it) }
|
||||
|
||||
override fun examples() = emptyList<String>()
|
||||
override fun invoke(buffer: PacketByteBuf, name: String): IntSpeciesFeature? {
|
||||
return if (name in keys) {
|
||||
IntSpeciesFeature(name, buffer.readInt())
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(pokemon: Pokemon): IntSpeciesFeature? {
|
||||
return get(pokemon) ?: default?.let { IntSpeciesFeature(keys.first(), it) }
|
||||
}
|
||||
|
||||
override fun invoke(nbt: NbtCompound): IntSpeciesFeature? {
|
||||
return if (nbt.contains(keys.first())) {
|
||||
IntSpeciesFeature(keys.first(), nbt.getInt(keys.first()))
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun invoke(json: JsonObject): IntSpeciesFeature? {
|
||||
return if (json.has(keys.first())) {
|
||||
IntSpeciesFeature(keys.first(), json.get(keys.first()).asInt)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun get(pokemon: Pokemon) = pokemon.features.filterIsInstance<IntSpeciesFeature>().find { it.name in keys }
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
|
||||
buffer.writeNullable(default) { _, value -> buffer.writeInt(value) }
|
||||
buffer.writeInt(min)
|
||||
buffer.writeInt(max)
|
||||
buffer.writeNullable(display) { _, value -> value.encode(buffer) }
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
keys = buffer.readList { buffer.readString() }
|
||||
default = buffer.readNullable { buffer.readInt() }
|
||||
min = buffer.readInt()
|
||||
max = buffer.readInt()
|
||||
display = buffer.readNullable { DisplayData().also { it.decode(buffer) } }
|
||||
}
|
||||
|
||||
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<IntSpeciesFeature>? {
|
||||
return display?.let {
|
||||
BarSummarySpeciesFeatureRenderer(
|
||||
name = keys.first(),
|
||||
displayName = it.name.asTranslated(),
|
||||
min = min,
|
||||
max = max,
|
||||
colour = it.colour,
|
||||
underlay = it.underlay ?: cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
|
||||
overlay = it.overlay ?: cobblemonResource("textures/gui/summary/summary_stats_generic_overlay.png"),
|
||||
pokemon = pokemon
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
-4
@@ -8,10 +8,13 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -38,17 +41,24 @@ object SpeciesFeatureAssignments : JsonDataRegistry<SpeciesFeatureAssignment> {
|
||||
override val typeToken = TypeToken.get(SpeciesFeatureAssignment::class.java)
|
||||
override val resourcePath = "species_feature_assignments"
|
||||
|
||||
private val assignments = mutableMapOf<String, MutableSet<String>>()
|
||||
private val assignments = mutableMapOf<Identifier, MutableSet<String>>()
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
player.sendPacket(SpeciesFeatureAssignmentSyncPacket(assignments))
|
||||
}
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureAssignment>) {
|
||||
data.values.forEach {
|
||||
it.pokemon.forEach { pokemon ->
|
||||
assignments.getOrPut(pokemon) { mutableSetOf() }.addAll(it.features)
|
||||
assignments.getOrPut(pokemon.asIdentifierDefaultingNamespace()) { mutableSetOf() }.addAll(it.features)
|
||||
}
|
||||
}
|
||||
this.observable.emit(this)
|
||||
}
|
||||
|
||||
fun getFeatures(species: Species) = assignments[species.showdownId()] ?: emptySet()
|
||||
fun loadOnClient(data: Map<Identifier, MutableSet<String>>) {
|
||||
this.assignments.clear()
|
||||
this.assignments.putAll(data)
|
||||
}
|
||||
|
||||
fun getFeatures(species: Species) = assignments[species.resourceIdentifier] ?: emptySet()
|
||||
}
|
||||
+1
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokemon.feature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A provider of [SpeciesFeature]s. These must be registered via [SpeciesFeatures] either programmatically or
|
||||
|
||||
+13
-1
@@ -8,13 +8,17 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -22,6 +26,7 @@ import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* A registry for [SpeciesFeatureProvider]s. This is the backbone of species-specific data such as
|
||||
@@ -48,11 +53,15 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
|
||||
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
|
||||
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
|
||||
override val resourcePath: String = "species_features"
|
||||
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
override fun sync(player: ServerPlayerEntity) {
|
||||
player.sendPacket(StandardSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
|
||||
}
|
||||
|
||||
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
|
||||
resourceFeatures.keys.toList().forEach(this::unregister)
|
||||
@@ -62,6 +71,9 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
|
||||
fun getCodeFeature(name: String) = resourceFeatures[name]
|
||||
fun getResourceFeature(name: String) = codeFeatures[name]
|
||||
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
|
||||
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
|
||||
codeFeatures.putAll(entries.map { it.toPair() })
|
||||
}
|
||||
|
||||
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
|
||||
fun getFeaturesFor(species: Species): List<SpeciesFeatureProvider<*>> {
|
||||
|
||||
+10
-1
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.google.gson.JsonObject
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A species feature value that is a string value.
|
||||
@@ -22,7 +23,7 @@ import net.minecraft.nbt.NbtCompound
|
||||
class StringSpeciesFeature(
|
||||
override val name: String,
|
||||
var value: String
|
||||
) : SpeciesFeature, CustomPokemonProperty {
|
||||
) : SynchronizedSpeciesFeature, CustomPokemonProperty {
|
||||
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
|
||||
pokemonNBT.putString(name, value)
|
||||
return pokemonNBT
|
||||
@@ -43,6 +44,14 @@ class StringSpeciesFeature(
|
||||
return this
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(value)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
value = buffer.readString()
|
||||
}
|
||||
|
||||
override fun asString() = "$name=$value"
|
||||
|
||||
override fun apply(pokemon: Pokemon) {
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
|
||||
/**
|
||||
* A species feature that can be synchronized to the client. These must be managed by a [SynchronizedSpeciesFeatureProvider].
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SynchronizedSpeciesFeature : SpeciesFeature, Encodable, Decodable
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.feature
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/**
|
||||
* A species feature provider that will be synchronized to the client. For it to be renderable in the summary
|
||||
* screen of a Pokémon it must also provide a [SummarySpeciesFeatureRenderer] in [getRenderer]. Note that you
|
||||
* really don't want to initialize a renderer outside of that function call because if the server ever initializes
|
||||
* it then you're going to get crashes on dedicated servers (this is a bad thing).
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature> : SpeciesFeatureProvider<T>, Encodable, Decodable {
|
||||
var visible: Boolean
|
||||
operator fun invoke(buffer: PacketByteBuf, name: String): T?
|
||||
/** Gets the feature from this Pokémon, if it has been created yet. */
|
||||
fun get(pokemon: Pokemon): T?
|
||||
/** Only run this from the client. */
|
||||
fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<T>?
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* Effect details for the actual particles of a particle effect.
|
||||
@@ -29,7 +28,7 @@ import net.minecraft.util.math.Vec3d
|
||||
* @since January 4th, 2023
|
||||
*/
|
||||
class BedrockParticle(
|
||||
var texture: Identifier = Identifier("minecraft:textures/particle/bubble.png"),
|
||||
var texture: Identifier = Identifier("minecraft:textures/particles/bubble.png"),
|
||||
var material: ParticleMaterial = ParticleMaterial.ALPHA,
|
||||
var uvMode: ParticleUVMode = StaticParticleUVMode(),
|
||||
var sizeX: Expression = NumberExpression(0.15),
|
||||
@@ -44,48 +43,106 @@ class BedrockParticle(
|
||||
var cameraMode: ParticleCameraMode = RotateXYZCameraMode(),
|
||||
var tinting: ParticleTinting = ExpressionParticleTinting(),
|
||||
var collision: ParticleCollision = ParticleCollision(),
|
||||
var environmentLighting: Boolean = false
|
||||
var environmentLighting: Boolean = false,
|
||||
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var timeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf())
|
||||
) {
|
||||
class ExpressionSet(
|
||||
val sizeX: Expression,
|
||||
val sizeY: Expression,
|
||||
val maxAge: Expression,
|
||||
val killExpression: Expression,
|
||||
val updateExpressions: MutableList<Expression>,
|
||||
val renderExpressions: MutableList<Expression>
|
||||
)
|
||||
|
||||
/*
|
||||
"creation_event": "event_sendspark",
|
||||
"expiration_event": ["event_spark", "event_afterspark", "event_capturestar"],
|
||||
"timeline": {
|
||||
"21.00": "event_spark"
|
||||
}
|
||||
*/
|
||||
|
||||
class EventSet(
|
||||
val creationEvents: MutableList<SimpleEventTrigger>,
|
||||
val expirationEvents: MutableList<SimpleEventTrigger>,
|
||||
val timeline: EventTriggerTimeline
|
||||
)
|
||||
|
||||
// kill plane maybe
|
||||
companion object {
|
||||
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
|
||||
val EXPRESSION_SET_CODEC = RecordCodecBuilder.create<ExpressionSet> { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
|
||||
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
|
||||
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
|
||||
EXPRESSION_CODEC.fieldOf("sizeX").forGetter { it.sizeX },
|
||||
EXPRESSION_CODEC.fieldOf("sizeY").forGetter { it.sizeY },
|
||||
EXPRESSION_CODEC.fieldOf("maxAge").forGetter { it.maxAge },
|
||||
EXPRESSION_CODEC.fieldOf("killExpression").forGetter { it.killExpression },
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions },
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions }
|
||||
).apply(instance, ::ExpressionSet)
|
||||
}
|
||||
|
||||
val EVENT_SET_CODEC = RecordCodecBuilder.create<EventSet> { instance ->
|
||||
instance.group(
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
|
||||
EventTriggerTimeline.CODEC.fieldOf("timeline").forGetter { it.timeline }
|
||||
).apply(instance, ::EventSet)
|
||||
}
|
||||
|
||||
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
|
||||
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
|
||||
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
|
||||
EXPRESSION_SET_CODEC.fieldOf("expressionSet").forGetter {
|
||||
ExpressionSet(
|
||||
it.sizeX,
|
||||
it.sizeY,
|
||||
it.maxAge,
|
||||
it.killExpression,
|
||||
it.updateExpressions,
|
||||
it.renderExpressions
|
||||
)
|
||||
},
|
||||
ParticleMotion.codec.fieldOf("motion").forGetter { it.motion },
|
||||
ParticleRotation.codec.fieldOf("rotation").forGetter { it.rotation },
|
||||
ParticleViewDirection.codec.fieldOf("viewDirection").forGetter { it.viewDirection },
|
||||
ParticleCameraMode.codec.fieldOf("cameraMode").forGetter { it.cameraMode },
|
||||
ParticleTinting.codec.fieldOf("tinting").forGetter { it.tinting },
|
||||
ParticleCollision.CODEC.fieldOf("collision").forGetter { it.collision },
|
||||
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting }
|
||||
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting },
|
||||
EVENT_SET_CODEC.fieldOf("eventSet").forGetter {
|
||||
EventSet(
|
||||
it.creationEvents,
|
||||
it.expirationEvents,
|
||||
it.timeline
|
||||
)
|
||||
}
|
||||
).apply(instance) {
|
||||
texture, materialStr, uvMode, sizeX, sizeY, maxAge, killExpression, updateExpressions,
|
||||
renderExpressions, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting ->
|
||||
texture, materialStr, uvMode, expressionSet, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting, eventSet ->
|
||||
BedrockParticle(
|
||||
texture = texture,
|
||||
material = ParticleMaterial.valueOf(materialStr),
|
||||
uvMode = uvMode,
|
||||
sizeX = sizeX,
|
||||
sizeY = sizeY,
|
||||
maxAge = maxAge,
|
||||
killExpression = killExpression,
|
||||
updateExpressions = updateExpressions,
|
||||
renderExpressions = renderExpressions,
|
||||
sizeX = expressionSet.sizeX,
|
||||
sizeY = expressionSet.sizeY,
|
||||
maxAge = expressionSet.maxAge,
|
||||
killExpression = expressionSet.killExpression,
|
||||
updateExpressions = expressionSet.updateExpressions,
|
||||
renderExpressions = expressionSet.renderExpressions,
|
||||
motion = motion,
|
||||
rotation = rotation,
|
||||
viewDirection = viewDirection,
|
||||
cameraMode = cameraMode,
|
||||
tinting = tinting,
|
||||
collision = collision,
|
||||
environmentLighting = environmentLighting
|
||||
environmentLighting = environmentLighting,
|
||||
creationEvents = eventSet.creationEvents,
|
||||
expirationEvents = eventSet.expirationEvents,
|
||||
timeline = eventSet.timeline
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -108,6 +165,9 @@ class BedrockParticle(
|
||||
ParticleTinting.writeToBuffer(buffer, tinting)
|
||||
collision.writeToBuffer(buffer)
|
||||
buffer.writeBoolean(environmentLighting)
|
||||
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
|
||||
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
|
||||
timeline.encode(buffer)
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -127,5 +187,8 @@ class BedrockParticle(
|
||||
tinting = ParticleTinting.readFromBuffer(buffer)
|
||||
collision.readFromBuffer(buffer)
|
||||
environmentLighting = buffer.readBoolean()
|
||||
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
timeline.decode(buffer)
|
||||
}
|
||||
}
|
||||
+11
-4
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.snowstorm
|
||||
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.ListCodec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import com.mojang.serialization.codecs.UnboundedMapCodec
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
@@ -27,7 +29,8 @@ class BedrockParticleEffect(
|
||||
var emitter: BedrockParticleEmitter = BedrockParticleEmitter(),
|
||||
var particle: BedrockParticle = BedrockParticle(),
|
||||
var curves: MutableList<MoLangCurve> = mutableListOf(),
|
||||
var space: ParticleSpace = ParticleSpace()
|
||||
var space: ParticleSpace = ParticleSpace(),
|
||||
var events: MutableMap<String, ParticleEvent> = mutableMapOf()
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<BedrockParticleEffect> = RecordCodecBuilder.create { instance ->
|
||||
@@ -36,14 +39,16 @@ class BedrockParticleEffect(
|
||||
BedrockParticleEmitter.CODEC.fieldOf("emitter").forGetter { it.emitter },
|
||||
BedrockParticle.CODEC.fieldOf("particle").forGetter { it.particle },
|
||||
ListCodec(MoLangCurve.codec).fieldOf("curves").forGetter { it.curves },
|
||||
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space }
|
||||
).apply(instance) { id, emitter, particle, curves, space ->
|
||||
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space },
|
||||
UnboundedMapCodec(PrimitiveCodec.STRING, ParticleEvent.CODEC).fieldOf("events").forGetter { it.events }
|
||||
).apply(instance) { id, emitter, particle, curves, space, events ->
|
||||
BedrockParticleEffect(
|
||||
id = id,
|
||||
emitter = emitter,
|
||||
particle = particle,
|
||||
curves = curves.toMutableList(),
|
||||
space = space
|
||||
space = space,
|
||||
events = events
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -55,6 +60,7 @@ class BedrockParticleEffect(
|
||||
particle.writeToBuffer(buffer)
|
||||
buffer.writeCollection(curves) { pb, curve -> MoLangCurve.writeToBuffer(buffer, curve) }
|
||||
space.writeToBuffer(buffer)
|
||||
buffer.writeMap(events, { _, v -> buffer.writeString(v) }) { _, event -> event.encode(buffer) }
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -63,5 +69,6 @@ class BedrockParticleEffect(
|
||||
particle.readFromBuffer(buffer)
|
||||
curves = buffer.readList { MoLangCurve.readFromBuffer(buffer) }
|
||||
space.readFromBuffer(buffer)
|
||||
events = buffer.readMap({ buffer.readString() }) { ParticleEvent().also { it.decode(buffer) } }
|
||||
}
|
||||
}
|
||||
+29
-4
@@ -29,7 +29,12 @@ class BedrockParticleEmitter(
|
||||
var updateExpressions: MutableList<Expression> = mutableListOf(),
|
||||
var rate: ParticleEmitterRate = InstantParticleEmitterRate(),
|
||||
var shape: ParticleEmitterShape = SphereParticleEmitterShape(),
|
||||
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0))
|
||||
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0)),
|
||||
var eventTimeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
|
||||
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
|
||||
var travelDistanceEvents: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
|
||||
var loopingTravelDistanceEvents: MutableList<LoopingTravelDistanceEventTrigger> = mutableListOf()
|
||||
) {
|
||||
companion object {
|
||||
val CODEC: Codec<BedrockParticleEmitter> = RecordCodecBuilder.create { instance ->
|
||||
@@ -38,14 +43,24 @@ class BedrockParticleEmitter(
|
||||
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
|
||||
ParticleEmitterRate.codec.fieldOf("rate").forGetter { it.rate },
|
||||
ParticleEmitterShape.codec.fieldOf("shape").forGetter { it.shape },
|
||||
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime }
|
||||
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime ->
|
||||
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime },
|
||||
EventTriggerTimeline.CODEC.fieldOf("eventTimeline").forGetter { it.eventTimeline },
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
|
||||
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
|
||||
EventTriggerTimeline.CODEC.fieldOf("travelDistanceEvents").forGetter { it.travelDistanceEvents },
|
||||
ListCodec(LoopingTravelDistanceEventTrigger.CODEC).fieldOf("loopingTravelDistanceEvents").forGetter { it.loopingTravelDistanceEvents }
|
||||
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
|
||||
BedrockParticleEmitter(
|
||||
startExpressions = startExpressions,
|
||||
updateExpressions = updateExpressions,
|
||||
shape = shape,
|
||||
rate = rate,
|
||||
lifetime = lifetime
|
||||
lifetime = lifetime,
|
||||
eventTimeline = eventTimeline,
|
||||
creationEvents = creationEvents,
|
||||
expirationEvents = expirationEvents,
|
||||
travelDistanceEvents = travelDistanceEvents,
|
||||
loopingTravelDistanceEvents = loopingTravelDistanceEvents
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -57,6 +72,11 @@ class BedrockParticleEmitter(
|
||||
ParticleEmitterRate.writeToBuffer(buffer, rate)
|
||||
ParticleEmitterShape.writeToBuffer(buffer, shape)
|
||||
ParticleEmitterLifetime.writeToBuffer(buffer, lifetime)
|
||||
eventTimeline.encode(buffer)
|
||||
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
|
||||
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
|
||||
travelDistanceEvents.encode(buffer)
|
||||
buffer.writeCollection(loopingTravelDistanceEvents) { pb, event -> event.encode(pb) }
|
||||
}
|
||||
|
||||
fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -65,5 +85,10 @@ class BedrockParticleEmitter(
|
||||
rate = ParticleEmitterRate.readFromBuffer(buffer)
|
||||
shape = ParticleEmitterShape.readFromBuffer(buffer)
|
||||
lifetime = ParticleEmitterLifetime.readFromBuffer(buffer)
|
||||
eventTimeline.decode(buffer)
|
||||
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
|
||||
travelDistanceEvents.decode(buffer)
|
||||
loopingTravelDistanceEvents = buffer.readList { LoopingTravelDistanceEventTrigger(0.0, mutableListOf()).also { it.decode(buffer) } }
|
||||
}
|
||||
}
|
||||
+5
@@ -136,6 +136,11 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
|
||||
val interval = activeTimeValue + sleepTime
|
||||
val displacement = emitterAge % interval
|
||||
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
|
||||
|
||||
if (emitterAge > activeTimeValue && sleepTime == 0.0) {
|
||||
return ParticleEmitterAction.STOP
|
||||
}
|
||||
|
||||
return if (displacement < activeTimeValue) {
|
||||
ParticleEmitterAction.GO
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -145,7 +145,7 @@ class PointParticleEmitterShape(
|
||||
override val type = ParticleEmitterShapeType.POINT
|
||||
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
|
||||
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3d {
|
||||
return getNewParticlePosition(runtime, entity) // Questionable
|
||||
return Vec3d.ZERO
|
||||
}
|
||||
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
|
||||
@@ -0,0 +1,174 @@
|
||||
/*
|
||||
* 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.snowstorm
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.util.asExpressionLike
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* An event that can be referenced from various particle event triggers. The events are not necessarily particles,
|
||||
* but can be combinations of particles, sounds, and MoLang expressions.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class ParticleEvent(
|
||||
var particleEffect: EventParticleEffect? = null,
|
||||
var soundEffect: EventSoundEffect? = null,
|
||||
var expression: ExpressionLike? = null
|
||||
): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC: Codec<ParticleEvent> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
EventParticleEffect.CODEC.optionalFieldOf("particle_effect", null).forGetter { it.particleEffect },
|
||||
EventSoundEffect.CODEC.optionalFieldOf("sound_effect", null).forGetter { it.soundEffect },
|
||||
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
|
||||
).apply(instance) { particleEffect, soundEffect, expression ->
|
||||
ParticleEvent(
|
||||
particleEffect,
|
||||
soundEffect,
|
||||
expression?.asExpressionLike()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeNullable(particleEffect) { pb, effect ->
|
||||
pb.writeIdentifier(effect.effect)
|
||||
pb.writeEnumConstant(effect.type)
|
||||
pb.writeNullable(effect.expression) { pb, expr -> pb.writeString(expr.toString()) }
|
||||
}
|
||||
buffer.writeNullable(soundEffect) { pb, effect -> pb.writeIdentifier(effect.sound) }
|
||||
buffer.writeNullable(expression) { pb, expr -> pb.writeString(expr.toString()) }
|
||||
}
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
particleEffect = buffer.readNullable { pb -> EventParticleEffect(
|
||||
pb.readIdentifier(),
|
||||
pb.readEnumConstant(EventParticleEffect.EventParticleType::class.java),
|
||||
pb.readNullable { pb.readString().asExpressionLike() }
|
||||
) }
|
||||
soundEffect = buffer.readNullable { pb -> EventSoundEffect(pb.readIdentifier()) }
|
||||
expression = buffer.readNullable { pb -> pb.readString().asExpressionLike() }
|
||||
}
|
||||
|
||||
fun run(storm: ParticleStorm, particle: SnowstormParticle?) {
|
||||
particleEffect?.let { effect ->
|
||||
val bedrockParticleEffect = BedrockParticleEffectRepository.getEffect(effect.effect) ?: return@let
|
||||
val rootMatrix = when (effect.type) {
|
||||
EventParticleEffect.EventParticleType.EMITTER -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
|
||||
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
|
||||
EventParticleEffect.EventParticleType.PARTICLE,
|
||||
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
|
||||
}
|
||||
|
||||
val sourceVelocity = when (effect.type) {
|
||||
EventParticleEffect.EventParticleType.EMITTER -> storm.sourceVelocity().let { { it } }
|
||||
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.sourceVelocity
|
||||
EventParticleEffect.EventParticleType.PARTICLE -> { { Vec3d.ZERO } }
|
||||
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getVelocityX(), it.getVelocityY(), it.getVelocityZ()) } ?: Vec3d.ZERO).let { { it } }
|
||||
}
|
||||
|
||||
val newStorm = ParticleStorm(
|
||||
effect = bedrockParticleEffect,
|
||||
matrixWrapper = rootMatrix,
|
||||
world = storm.world,
|
||||
sourceVelocity = sourceVelocity,
|
||||
sourceAlive = storm.sourceAlive,
|
||||
sourceVisible = storm.sourceVisible,
|
||||
onDespawn = {},
|
||||
runtime = MoLangRuntime().setup().also { it.environment.structs["query"] = storm.runtime.environment.getQueryStruct() },
|
||||
entity = storm.entity
|
||||
)
|
||||
|
||||
effect.expression?.resolve(newStorm.runtime)
|
||||
|
||||
newStorm.spawn()
|
||||
}
|
||||
soundEffect?.let { effect ->
|
||||
val position = particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())
|
||||
val world = storm.world
|
||||
val soundEvent = SoundEvent.of(effect.sound)
|
||||
world.playSound(position.x, position.y, position.z, soundEvent, SoundCategory.NEUTRAL, 1F, 1F, true)
|
||||
}
|
||||
expression?.resolve(storm.runtime)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A particle component of a [ParticleEvent]. It contains the effect to play, the spawning type, and a pre-run
|
||||
* expression to run in the new storm's runtime.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class EventParticleEffect(
|
||||
val effect: Identifier,
|
||||
val type: EventParticleType,
|
||||
val expression: ExpressionLike? = null
|
||||
) {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<EventParticleEffect> { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("effect").forGetter { it.effect },
|
||||
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
|
||||
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
|
||||
).apply(instance) { effect, type, expression ->
|
||||
EventParticleEffect(
|
||||
effect,
|
||||
EventParticleType.valueOf(type),
|
||||
expression?.asExpressionLike()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum class EventParticleType {
|
||||
EMITTER,
|
||||
EMITTER_BOUND,
|
||||
PARTICLE,
|
||||
PARTICLE_WITH_VELOCITY
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A simple sound effect component of a [ParticleEvent]. Plays a sound at the particle location, or if run from an
|
||||
* emitter event, at the emitter's location.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class EventSoundEffect(
|
||||
val sound: Identifier,
|
||||
) {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<EventSoundEffect> { instance ->
|
||||
instance.group(
|
||||
Identifier.CODEC.fieldOf("sound").forGetter { it.sound }
|
||||
).apply(instance, ::EventSoundEffect)
|
||||
}
|
||||
}
|
||||
}
|
||||
+118
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
* 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.snowstorm
|
||||
|
||||
import com.cobblemon.mod.common.api.net.Decodable
|
||||
import com.cobblemon.mod.common.api.net.Encodable
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import com.mojang.serialization.codecs.UnboundedMapCodec
|
||||
import net.minecraft.network.PacketByteBuf
|
||||
|
||||
/*
|
||||
* The different references to events that can be triggered by an effect.
|
||||
*/
|
||||
|
||||
/**
|
||||
* It really is simple.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class SimpleEventTrigger(var event: String): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<SimpleEventTrigger> { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.STRING.fieldOf("event").forGetter { it.event }
|
||||
).apply(instance, ::SimpleEventTrigger)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeString(event)
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
event = buffer.readString()
|
||||
}
|
||||
|
||||
fun trigger(storm: ParticleStorm, particle: SnowstormParticle?) {
|
||||
val event = storm.effect.events[event] ?: return
|
||||
event.run(storm, particle)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event trigger that outlines specific times at which point groups of effects will play. This can be used
|
||||
* on the basis of time but is also used in the case of travel distance.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class EventTriggerTimeline(var map: MutableMap<Double, MutableList<String>>): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<EventTriggerTimeline> { instance ->
|
||||
instance.group(
|
||||
UnboundedMapCodec(PrimitiveCodec.DOUBLE, PrimitiveCodec.STRING.listOf()).fieldOf("map").forGetter { it.map }
|
||||
).apply(instance, ::EventTriggerTimeline)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeMap(map, { pb, k -> pb.writeDouble(k) }, { pb, v -> pb.writeCollection(v) { _, s -> pb.writeString(s) } })
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
map = buffer.readMap({ pb -> pb.readDouble() }, { pb -> pb.readList { pb.readString() } })
|
||||
}
|
||||
|
||||
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousTime: Double, newTime: Double) {
|
||||
val events = map.entries.filter { it.key in previousTime..newTime }.flatMap { it.value }
|
||||
events.forEach { event ->
|
||||
val event = storm.effect.events[event] ?: return
|
||||
event.run(storm, particle)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An event trigger that takes the total distance travelled by the emitter and modulos it across [distance] to know
|
||||
* if we need to play it again.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since March 2nd, 2024
|
||||
*/
|
||||
class LoopingTravelDistanceEventTrigger(var distance: Double, var events: MutableList<String>): Encodable, Decodable {
|
||||
companion object {
|
||||
val CODEC = RecordCodecBuilder.create<LoopingTravelDistanceEventTrigger> { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.DOUBLE.fieldOf("distance").forGetter { it.distance },
|
||||
PrimitiveCodec.STRING.listOf().fieldOf("events").forGetter { it.events }
|
||||
).apply(instance, ::LoopingTravelDistanceEventTrigger)
|
||||
}
|
||||
}
|
||||
|
||||
override fun encode(buffer: PacketByteBuf) {
|
||||
buffer.writeDouble(distance)
|
||||
buffer.writeCollection(events) { _, s -> buffer.writeString(s) }
|
||||
}
|
||||
|
||||
override fun decode(buffer: PacketByteBuf) {
|
||||
distance = buffer.readDouble()
|
||||
events = buffer.readList { buffer.readString() }
|
||||
}
|
||||
|
||||
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousDistance: Double, currentDistance: Double) {
|
||||
if (previousDistance < distance && currentDistance >= distance) {
|
||||
events.mapNotNull { storm.effect.events[it] }.forEach { it.run(storm, particle) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,7 @@ class InwardsMotionDirection : ParticleMotionDirection {
|
||||
return if (particlePos == emitterPos) {
|
||||
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
|
||||
} else {
|
||||
particlePos.subtract(emitterPos)
|
||||
emitterPos.subtract(particlePos)
|
||||
}.normalize()
|
||||
}
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
|
||||
+3
-1
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
|
||||
import java.util.UUID
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
@@ -34,7 +36,7 @@ object CobblemonWorldSpawnerManager : SpawnerManager() {
|
||||
|
||||
fun onPlayerLogin(player: ServerPlayerEntity) {
|
||||
// Disables spawning
|
||||
if (!Cobblemon.config.enableSpawning) {
|
||||
if (!Cobblemon.config.enableSpawning || server()?.gameRules?.getBoolean(CobblemonGameRules.DO_POKEMON_SPAWNING) == false) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@ import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
|
||||
import com.cobblemon.mod.common.api.spawning.spawner.TickingSpawner
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
|
||||
|
||||
/**
|
||||
* A manager of various spawners. This is a class in which you should register
|
||||
@@ -49,7 +51,7 @@ open class SpawnerManager {
|
||||
|
||||
open fun onServerTick() {
|
||||
// Disables spawning
|
||||
if (!Cobblemon.config.enableSpawning) {
|
||||
if (!Cobblemon.config.enableSpawning || server()?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
|
||||
return
|
||||
}
|
||||
influences.removeIf { it.isExpired() }
|
||||
|
||||
+10
-3
@@ -30,9 +30,16 @@ abstract class SingleEntitySpawnAction<T : Entity>(
|
||||
override fun run(): EntitySpawnResult? {
|
||||
val e = createEntity() ?: return null
|
||||
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
|
||||
entity.emit(e)
|
||||
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
|
||||
return EntitySpawnResult(listOf(e))
|
||||
var shouldSpawn = false
|
||||
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = {
|
||||
ctx.world.spawnEntity(e)
|
||||
shouldSpawn = true
|
||||
})
|
||||
|
||||
return if (shouldSpawn) {
|
||||
this.entity.emit(e)
|
||||
EntitySpawnResult(listOf(e))
|
||||
} else null
|
||||
}
|
||||
|
||||
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* 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.influence
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
|
||||
import net.minecraft.registry.tag.BlockTags
|
||||
|
||||
/**
|
||||
* Implements a [SpawningInfluence] that prevents Pokémon spawning on certain blocks.
|
||||
* This ensures that Pokémon don't spawn in areas that could disrupt player-built structures or mechanisms.
|
||||
* The influence checks the block directly above the potential spawn location; if the block is in the restricted list,
|
||||
* the spawn is disallowed.
|
||||
*
|
||||
* @author Aethen
|
||||
* @since February 20th, 2024
|
||||
*/
|
||||
open class RestrictedSpawnBlocksInfluence
|
||||
: SpawningInfluence {
|
||||
|
||||
// The list of blocks that Pokémon are not allowed to spawn on.
|
||||
private val restrictedBlocks = BlockTags.RAILS
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Affects the spawnable status of a Pokémon spawn detail.
|
||||
* If the block directly above the spawn location is in the restricted list, the spawn is disallowed.
|
||||
*
|
||||
* @param detail The Pokémon spawn detail to be affected.
|
||||
* @param ctx The context of the spawning event.
|
||||
* @return `true` if the spawn is allowed, `false` if it is disallowed.
|
||||
*/
|
||||
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
|
||||
// If the spawn detail is not a Pokémon, it is not affected by this influence.
|
||||
if (detail.type != "pokemon") return true
|
||||
|
||||
// Get the position of the potential spawn location.
|
||||
val pokemonSpawnPos = ctx.position
|
||||
|
||||
// Examine the block immediately above the spawn block to check for a rail
|
||||
// Potentially need a refactor if we ever want to check the spawn block itself.
|
||||
return ctx.world.getBlockState(pokemonSpawnPos.withY(pokemonSpawnPos.y + 1)).isIn(restrictedBlocks).not()
|
||||
}
|
||||
}
|
||||
@@ -63,9 +63,6 @@ abstract class AreaSpawner(
|
||||
|
||||
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
|
||||
val area = getArea(cause)
|
||||
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
|
||||
return null
|
||||
}
|
||||
val constrainedArea = if (area != null) constrainArea(area) else null
|
||||
if (constrainedArea != null) {
|
||||
|
||||
|
||||
+22
-3
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
|
||||
import com.cobblemon.mod.common.api.spawning.SpawnerManager
|
||||
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
|
||||
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.RestrictedSpawnBlocksInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
|
||||
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
|
||||
import com.cobblemon.mod.common.util.mutableLazy
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
/**
|
||||
@@ -26,14 +28,31 @@ import net.minecraft.server.network.ServerPlayerEntity
|
||||
* @since February 14th, 2022
|
||||
*/
|
||||
object PlayerSpawnerFactory {
|
||||
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
|
||||
var spawns: SpawnPool by mutableLazy {
|
||||
CobblemonSpawnPools.WORLD_SPAWN_POOL
|
||||
}
|
||||
|
||||
/**
|
||||
* The list of influences for a new [PlayerSpawner].
|
||||
* The influences will be added to the spawner in the order they are added to this list.
|
||||
*
|
||||
* [PlayerLevelRangeInfluence] adjusts the level range of Pokémon that can spawn based on the player's level.
|
||||
* [RestrictedSpawnBlocksInfluence] prevents Pokémon from spawning on certain blocks.
|
||||
*/
|
||||
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({
|
||||
PlayerLevelRangeInfluence(
|
||||
it,
|
||||
variation = 5
|
||||
)
|
||||
}, { RestrictedSpawnBlocksInfluence() })
|
||||
|
||||
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
|
||||
val influences = influenceBuilders.mapNotNull { it(player) }
|
||||
return PlayerSpawner(player, spawns, spawnerManager).also {
|
||||
it.influences.addAll(influences)
|
||||
it.influences.addAll(CobblemonSpawnRules.rules.values.filter(SpawnRule::enabled).flatMap(SpawnRule::components))
|
||||
it.influences.addAll(
|
||||
CobblemonSpawnRules.rules.values.filter(SpawnRule::enabled).flatMap(SpawnRule::components)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-11
@@ -6,24 +6,19 @@
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.config.starter
|
||||
package com.cobblemon.mod.common.api.starter
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import net.minecraft.text.Text
|
||||
|
||||
data class StarterCategory(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val displayName: Text,
|
||||
val pokemon: List<PokemonProperties>
|
||||
) {
|
||||
fun asRenderableStarterCategory() = RenderableStarterCategory(name, displayName, pokemon.map { it.asRenderablePokemon() })
|
||||
}
|
||||
)
|
||||
|
||||
data class RenderableStarterCategory(
|
||||
val name: String,
|
||||
val displayName: String,
|
||||
val displayName: Text,
|
||||
val pokemon: List<RenderablePokemon>
|
||||
) {
|
||||
val displayNameText = displayName.asTranslated()
|
||||
}
|
||||
)
|
||||
@@ -8,12 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.starter
|
||||
|
||||
import com.cobblemon.mod.common.config.starter.StarterCategory
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
interface StarterHandler {
|
||||
fun getStarterList(player: ServerPlayerEntity): List<StarterCategory>
|
||||
fun getStarters(player: ServerPlayerEntity): Map<Identifier, StarterCategory>
|
||||
fun handleJoin(player: ServerPlayerEntity)
|
||||
fun requestStarterChoice(player: ServerPlayerEntity)
|
||||
fun chooseStarter(player: ServerPlayerEntity, categoryName: String, index: Int)
|
||||
fun chooseStarter(player: ServerPlayerEntity, categoryId: Identifier, index: Int)
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* 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.starter
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.data.DataRegistry
|
||||
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.starter.CobblemonStarterHandler
|
||||
import com.cobblemon.mod.common.util.adapters.PlainJsonTextAdapter
|
||||
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* The registry for starter Pokémon.
|
||||
*
|
||||
* The data here is used by the [CobblemonStarterHandler].
|
||||
*
|
||||
* 3rd party is not enforced to respect this registry.
|
||||
*/
|
||||
object StarterRegistry : JsonDataRegistry<StarterCategory> {
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
.disableHtmlEscaping()
|
||||
.registerTypeAdapter(Text::class.java, PlainJsonTextAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.create()
|
||||
override val typeToken: TypeToken<StarterCategory> = TypeToken.get(StarterCategory::class.java)
|
||||
override val id: Identifier = cobblemonResource("starter")
|
||||
override val resourcePath: String = id.path
|
||||
override val type: ResourceType = ResourceType.SERVER_DATA
|
||||
override val observable: SimpleObservable<out DataRegistry> = SimpleObservable()
|
||||
private val categories = hashMapOf<Identifier, StarterCategory>()
|
||||
|
||||
// We let the default starter handler sync the client
|
||||
// This is to prevent unnecessary packets depending on impl
|
||||
override fun sync(player: ServerPlayerEntity) {}
|
||||
|
||||
override fun reload(data: Map<Identifier, StarterCategory>) {
|
||||
this.categories.clear()
|
||||
this.categories += data
|
||||
Cobblemon.LOGGER.info("Loaded {} starters ({} categories)", this.categories.values.sumOf { it.pokemon.size }, this.categories.size)
|
||||
}
|
||||
|
||||
fun all(): Map<Identifier, StarterCategory> = this.categories
|
||||
|
||||
fun keys(): Set<Identifier> = this.categories.keys
|
||||
|
||||
fun categories(): Collection<StarterCategory> = this.categories.values
|
||||
|
||||
fun categoryOf(key: Identifier): StarterCategory? = this.categories[key]
|
||||
|
||||
fun categoryOf(key: String): StarterCategory? = this.categories[key.asIdentifierDefaultingNamespace()]
|
||||
}
|
||||
+4
-2
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage.adapter.conversions
|
||||
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
@@ -98,8 +99,9 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
|
||||
result.level = nbt.getInt("Level")
|
||||
result.addExperience(SidemodExperienceSource("Reforged"), nbt.getInt("EXP"))
|
||||
result.setFriendship(nbt.getInt("Friendship"))
|
||||
result.ability = (result.form.abilities.find { it.template.name == nbt.getString("Ability") } ?: result.form.abilities.first())
|
||||
.template.create()
|
||||
Abilities.get(nbt.getString("Ability"))?.let { template ->
|
||||
result.updateAbility(template.create(forced = result.form.abilities.none { it.template == template }))
|
||||
}
|
||||
result.nature = Natures.getNature(Identifier(ReforgedNatures.values()[nbt.getInt("Nature")].name.lowercase())) ?: Natures.getRandomNature()
|
||||
result.mintedNature = Natures.getNature(Identifier(ReforgedNatures.values()[nbt.getInt("MintNature")].name.lowercase()))
|
||||
result.currentHealth = nbt.getInt("Health")
|
||||
|
||||
@@ -11,12 +11,14 @@ package com.cobblemon.mod.common.api.storage.party
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
|
||||
import com.cobblemon.mod.common.api.storage.pc.PCStore
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
@@ -123,6 +125,12 @@ open class PlayerPartyStore(
|
||||
|
||||
// Passive evolutions
|
||||
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
|
||||
val removeList = mutableListOf<Evolution>()
|
||||
pokemon.evolutionProxy.server().forEach {
|
||||
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
|
||||
removeList.add(it)
|
||||
}
|
||||
removeList.forEach { pokemon.evolutionProxy.server().remove(it) }
|
||||
}
|
||||
// Friendship
|
||||
// ToDo expand this down the line just a very basic implementation for the first releases
|
||||
|
||||
@@ -37,7 +37,7 @@ data class PlayerData(
|
||||
fun defaultData(forPlayer: UUID) = PlayerData(
|
||||
uuid = forPlayer,
|
||||
starterPrompted = false,
|
||||
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
|
||||
starterLocked = !Cobblemon.config.allowStarterOnJoin,
|
||||
starterSelected = false,
|
||||
starterUUID = null,
|
||||
keyItems = mutableSetOf(),
|
||||
|
||||
@@ -22,107 +22,15 @@ import net.minecraft.registry.tag.TagKey
|
||||
object CobblemonBiomeTags {
|
||||
|
||||
@JvmField
|
||||
val IS_DEEP_DARK = create("is_deep_dark")
|
||||
val IS_AUTUMN = create("has_season/autumn")
|
||||
@JvmField
|
||||
val IS_ARID = create("is_arid")
|
||||
val IS_SPRING = create("has_season/spring")
|
||||
@JvmField
|
||||
val IS_AUTUMN = create("is_autumn")
|
||||
@JvmField
|
||||
val IS_BADLANDS = create("is_badlands")
|
||||
@JvmField
|
||||
val IS_BAMBOO = create("is_bamboo")
|
||||
@JvmField
|
||||
val IS_CAVE = create("is_cave")
|
||||
@JvmField
|
||||
val IS_COAST = create("is_coast")
|
||||
@JvmField
|
||||
val IS_COLD = create("is_cold")
|
||||
@JvmField
|
||||
val IS_DEEP_OCEAN = create("is_deep_ocean")
|
||||
@JvmField
|
||||
val IS_DESERT = create("is_desert")
|
||||
@JvmField
|
||||
val IS_DRIPSTONE = create("is_dripstone")
|
||||
@JvmField
|
||||
val IS_FLORAL = create("is_floral")
|
||||
@JvmField
|
||||
val IS_FOREST = create("is_forest")
|
||||
@JvmField
|
||||
val IS_FREEZING = create("is_freezing")
|
||||
@JvmField
|
||||
val IS_FRESHWATER = create("is_freshwater")
|
||||
@JvmField
|
||||
val IS_COLD_OCEAN = create("is_cold_ocean")
|
||||
@JvmField
|
||||
val IS_FROZEN_OCEAN = create("is_frozen_ocean")
|
||||
@JvmField
|
||||
val IS_GLACIAL = create("is_glacial")
|
||||
@JvmField
|
||||
val IS_GRASSLAND = create("is_grassland")
|
||||
@JvmField
|
||||
val IS_HIGHLANDS = create("is_highlands")
|
||||
@JvmField
|
||||
val IS_HILLS = create("is_hills")
|
||||
@JvmField
|
||||
val IS_ISLAND = create("is_island")
|
||||
@JvmField
|
||||
val IS_JUNGLE = create("is_jungle")
|
||||
@JvmField
|
||||
val IS_LUKEWARM_OCEAN = create("is_lukewarm_ocean")
|
||||
@JvmField
|
||||
val IS_LUSH = create("is_lush")
|
||||
@JvmField
|
||||
val IS_MAGICAL = create("is_magical")
|
||||
@JvmField
|
||||
val IS_MOUNTAIN = create("is_mountain")
|
||||
@JvmField
|
||||
val IS_MUSHROOM = create("is_mushroom")
|
||||
@JvmField
|
||||
val IS_OVERWORLD = create("is_overworld")
|
||||
@JvmField
|
||||
val IS_PEAK = create("is_peak")
|
||||
@JvmField
|
||||
val IS_PLAINS = create("is_plains")
|
||||
@JvmField
|
||||
val IS_PLATEAU = create("is_plateau")
|
||||
@JvmField
|
||||
val IS_WARM_OCEAN = create("is_warm_ocean")
|
||||
@JvmField
|
||||
val IS_RIVER = create("is_river")
|
||||
@JvmField
|
||||
val IS_SANDY = create("is_sandy")
|
||||
@JvmField
|
||||
val IS_SAVANNA = create("is_savanna")
|
||||
@JvmField
|
||||
val IS_SKY = create("is_sky")
|
||||
@JvmField
|
||||
val IS_SNOWY_FOREST = create("is_snowy_forest")
|
||||
@JvmField
|
||||
val IS_SPOOKY = create("is_spooky")
|
||||
@JvmField
|
||||
val IS_SPRING = create("is_spring")
|
||||
@JvmField
|
||||
val IS_SUMMER = create("is_summer")
|
||||
@JvmField
|
||||
val IS_SWAMP = create("is_swamp")
|
||||
@JvmField
|
||||
val IS_TAIGA = create("is_taiga")
|
||||
val IS_SUMMER = create("has_season/summer")
|
||||
@JvmField
|
||||
val IS_TEMPERATE = create("is_temperate")
|
||||
@JvmField
|
||||
val IS_THERMAL = create("is_thermal")
|
||||
@JvmField
|
||||
val IS_TUNDRA = create("is_tundra")
|
||||
@JvmField
|
||||
val IS_VOID = create("is_void")
|
||||
@JvmField
|
||||
val IS_VOlCANIC = create("is_volcanic")
|
||||
@JvmField
|
||||
val IS_WINTER = create("is_winter")
|
||||
@JvmField
|
||||
val IS_SPARSE = create("is_sparse")
|
||||
@JvmField
|
||||
val IS_DENSE = create("is_dense")
|
||||
val IS_WINTER = create("has_season/winter")
|
||||
|
||||
// Has Feature tags
|
||||
@JvmField
|
||||
@@ -180,16 +88,5 @@ object CobblemonBiomeTags {
|
||||
@JvmField
|
||||
val HAS_WATER_STONE_ORE_RARE = create("has_ore/ore_water_stone_rare")
|
||||
|
||||
// ToDo remove in 1.3
|
||||
@Deprecated(message = "This tag no longer exists", level = DeprecationLevel.ERROR)
|
||||
@JvmField
|
||||
val IS_CRAG = create("is_crag")
|
||||
@Deprecated(message = "This tag no longer exists", level = DeprecationLevel.ERROR)
|
||||
@JvmField
|
||||
val IS_ICY = create("is_icy")
|
||||
@Deprecated(message = "This tag no longer exists", replaceWith = ReplaceWith("com.cobblemon.mod.common.api.tags.CobblemonBiomeTags.IS_CAVE"), level = DeprecationLevel.ERROR)
|
||||
@JvmField
|
||||
val IS_UNDERGROUND = create("is_underground")
|
||||
|
||||
private fun create(path: String) = TagKey.of(RegistryKeys.BIOME, cobblemonResource(path))
|
||||
}
|
||||
@@ -144,6 +144,12 @@ object CobblemonItemTags {
|
||||
@JvmField
|
||||
val POTTERY_SHERDS = create("decorated_pot_sherds")
|
||||
|
||||
@JvmField
|
||||
val ABILITY_CHANGERS = create("ability_changers")
|
||||
|
||||
@JvmField
|
||||
val IS_FRIENDSHIP_BOOSTER = create("is_friendship_booster")
|
||||
|
||||
private fun create(path: String) = TagKey.of(RegistryKeys.ITEM, cobblemonResource(path))
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.types.tera
|
||||
|
||||
import com.cobblemon.mod.common.api.data.ShowdownIdentifiable
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* The representation of the Pokémons Tera type.
|
||||
*
|
||||
* @see [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Terastal_phenomenon)
|
||||
*
|
||||
*/
|
||||
interface TeraType : ShowdownIdentifiable {
|
||||
|
||||
/**
|
||||
* The [Identifier] associated to this type.
|
||||
*/
|
||||
val id: Identifier
|
||||
|
||||
/**
|
||||
* If this tera type can be selected naturally.
|
||||
*/
|
||||
val legalAsStatic: Boolean
|
||||
|
||||
/**
|
||||
* The display name of this type.
|
||||
*/
|
||||
val displayName: Text
|
||||
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.types.tera
|
||||
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.api.types.tera.elemental.ElementalTypeTeraType
|
||||
import com.cobblemon.mod.common.api.types.tera.gimmick.StellarTeraType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* The registry of all [TeraType]s.
|
||||
*/
|
||||
@Suppress("unused")
|
||||
object TeraTypes {
|
||||
private val types = hashMapOf<Identifier, TeraType>()
|
||||
|
||||
@JvmStatic
|
||||
val NORMAL = this.create(cobblemonResource("normal"), ElementalTypeTeraType(ElementalTypes.NORMAL))
|
||||
|
||||
@JvmStatic
|
||||
val FIRE = this.create(cobblemonResource("fire"), ElementalTypeTeraType(ElementalTypes.FIRE))
|
||||
|
||||
@JvmStatic
|
||||
val WATER = this.create(cobblemonResource("water"), ElementalTypeTeraType(ElementalTypes.WATER))
|
||||
|
||||
@JvmStatic
|
||||
val GRASS = this.create(cobblemonResource("grass"), ElementalTypeTeraType(ElementalTypes.GRASS))
|
||||
|
||||
@JvmStatic
|
||||
val ELECTRIC = this.create(cobblemonResource("electric"), ElementalTypeTeraType(ElementalTypes.ELECTRIC))
|
||||
|
||||
@JvmStatic
|
||||
val ICE = this.create(cobblemonResource("ice"), ElementalTypeTeraType(ElementalTypes.ICE))
|
||||
|
||||
@JvmStatic
|
||||
val FIGHTING = this.create(cobblemonResource("fighting"), ElementalTypeTeraType(ElementalTypes.FIGHTING))
|
||||
|
||||
@JvmStatic
|
||||
val POISON = this.create(cobblemonResource("poison"), ElementalTypeTeraType(ElementalTypes.POISON))
|
||||
|
||||
@JvmStatic
|
||||
val GROUND = this.create(cobblemonResource("ground"), ElementalTypeTeraType(ElementalTypes.GROUND))
|
||||
|
||||
@JvmStatic
|
||||
val FLYING = this.create(cobblemonResource("flying"), ElementalTypeTeraType(ElementalTypes.FLYING))
|
||||
|
||||
@JvmStatic
|
||||
val PSYCHIC = this.create(cobblemonResource("psychic"), ElementalTypeTeraType(ElementalTypes.PSYCHIC))
|
||||
|
||||
@JvmStatic
|
||||
val BUG = this.create(cobblemonResource("bug"), ElementalTypeTeraType(ElementalTypes.BUG))
|
||||
|
||||
@JvmStatic
|
||||
val ROCK = this.create(cobblemonResource("rock"), ElementalTypeTeraType(ElementalTypes.ROCK))
|
||||
|
||||
@JvmStatic
|
||||
val GHOST = this.create(cobblemonResource("ghost"), ElementalTypeTeraType(ElementalTypes.GHOST))
|
||||
|
||||
@JvmStatic
|
||||
val DRAGON = this.create(cobblemonResource("dragon"), ElementalTypeTeraType(ElementalTypes.DRAGON))
|
||||
|
||||
@JvmStatic
|
||||
val DARK = this.create(cobblemonResource("dark"), ElementalTypeTeraType(ElementalTypes.DARK))
|
||||
|
||||
@JvmStatic
|
||||
val STEEL = this.create(cobblemonResource("steel"), ElementalTypeTeraType(ElementalTypes.STEEL))
|
||||
|
||||
@JvmStatic
|
||||
val FAIRY = this.create(cobblemonResource("fairy"), ElementalTypeTeraType(ElementalTypes.FAIRY))
|
||||
|
||||
@JvmStatic
|
||||
val STELLAR = this.create(StellarTeraType.ID, StellarTeraType())
|
||||
|
||||
/**
|
||||
* Pick a random [TeraType].
|
||||
*
|
||||
* @param legalOnly If [TeraType.legalAsStatic] should be respected.
|
||||
* @return The selected [TeraType].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun random(legalOnly: Boolean): TeraType {
|
||||
val possible = this.types.values
|
||||
if (legalOnly) {
|
||||
return possible.filter(TeraType::legalAsStatic).random()
|
||||
}
|
||||
return possible.random()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a [TeraType] by its [id].
|
||||
*
|
||||
* @param id The [Identifier] expected to match against a [TeraType.id].
|
||||
* @return The found [TeraType] or null.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun get(id: Identifier): TeraType? = this.types[id]
|
||||
|
||||
/**
|
||||
* Gets a [TeraType] by its [id].
|
||||
*
|
||||
* @param id The string representation of a [Identifier] if no namespace is present assumes Cobblemon' instead of Minecraft'.
|
||||
* @return The found [TeraType] or null.
|
||||
*/
|
||||
@JvmStatic
|
||||
fun get(id: String): TeraType? = this.get(cobblemonResource(id))
|
||||
|
||||
/**
|
||||
* Gets the corresponding tera type for a [ElementalType].
|
||||
*
|
||||
* @param type The [ElementalType] being checked.
|
||||
* @return The associated [TeraType].
|
||||
*/
|
||||
@JvmStatic
|
||||
fun forElementalType(type: ElementalType): TeraType = this.get(cobblemonResource(type.name))!! // it's safe to do
|
||||
|
||||
private fun create(id: Identifier, type: TeraType): TeraType {
|
||||
this.types[id] = type
|
||||
return type
|
||||
}
|
||||
}
|
||||
+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.types.tera.elemental
|
||||
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class ElementalTypeTeraType(val type: ElementalType) : TeraType {
|
||||
override val legalAsStatic: Boolean = true
|
||||
|
||||
override val id: Identifier = cobblemonResource(this.type.name)
|
||||
|
||||
override val displayName: Text = this.type.displayName
|
||||
|
||||
override fun showdownId(): String = this.type.name
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.types.tera.gimmick
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.types.tera.TeraType
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class StellarTeraType : TeraType {
|
||||
override val id: Identifier = ID
|
||||
|
||||
override val legalAsStatic: Boolean = false
|
||||
|
||||
override val displayName: Text = LANG
|
||||
|
||||
override fun showdownId(): String = ID.path
|
||||
|
||||
companion object {
|
||||
val ID = cobblemonResource("stellar")
|
||||
private val LANG = Text.translatable("${Cobblemon.MODID}.terra_type.stellar")
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,6 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpire
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.google.gson.GsonBuilder
|
||||
import java.time.Instant
|
||||
import java.util.Optional
|
||||
import java.util.UUID
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
@@ -139,7 +138,7 @@ object BattleRegistry {
|
||||
// 0 - 9, empty == 10
|
||||
packedTeamBuilder.append("${if (pk.dmaxLevel < 10) pk.dmaxLevel else ""},")
|
||||
// Teratype
|
||||
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.name},")
|
||||
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.showdownId()},")
|
||||
|
||||
team.add(packedTeamBuilder.toString())
|
||||
}
|
||||
|
||||
+20
-11
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
@@ -70,18 +71,26 @@ class DamageInstruction(
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
if (!causedFaint) {
|
||||
val pokemonEntity = battlePokemon.entity
|
||||
if (!causedFaint && pokemonEntity != null) {
|
||||
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
|
||||
pkt.sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.world.registryKey,
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
|
||||
battlePokemon.entity?.let { pokemonEntity ->
|
||||
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
|
||||
pkt.sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.world.registryKey,
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
if (pokemonEntity != null) {
|
||||
RunPosableMoLangPacket(pokemonEntity.id, setOf("q.particle('cobblemon:hit', 'target')")).sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.world.registryKey,
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.block
|
||||
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
class CoinPouchBlock(settings: Settings, val small: Boolean) : HorizontalFacingBlock(settings) {
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(NATURAL, false)
|
||||
.with(FACING, Direction.NORTH)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(
|
||||
state: BlockState?,
|
||||
world: BlockView?,
|
||||
pos: BlockPos?,
|
||||
context: ShapeContext?
|
||||
) = if (!small) super.getOutlineShape(state, world, pos, context) else {
|
||||
VoxelShapes.cuboid(0.3125, 0.0, 0.3125, 0.6875, 0.3125, 0.6875)
|
||||
}
|
||||
|
||||
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
var blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
ctx.placementDirections.forEach { direction ->
|
||||
if (direction.axis.isHorizontal) {
|
||||
blockState = blockState.with(FACING, direction.opposite) as BlockState
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
return if (direction == state.get(FACING) && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
|
||||
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, NATURAL)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val NATURAL: BooleanProperty = BooleanProperty.of("natural")
|
||||
}
|
||||
}
|
||||
@@ -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.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.HorizontalFacingBlock.*
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.BlockItem
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.ItemScatterer
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class DisplayCaseBlock(settings: Settings) : BlockWithEntity(settings) {
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(FACING, Direction.NORTH)
|
||||
}
|
||||
|
||||
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
var blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
ctx.placementDirections.forEach { direction ->
|
||||
if (direction.axis.isHorizontal) {
|
||||
blockState = blockState.with(FACING, direction) as BlockState
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): DisplayCaseBlockEntity {
|
||||
return DisplayCaseBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
return if (direction == state.get(FACING) && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
|
||||
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
|
||||
val result = entity.updateItem(player, hand)
|
||||
if ((hit.side != Direction.UP && hit.side != Direction.DOWN) && result == ActionResult.SUCCESS) {
|
||||
world.setBlockState(pos, state.with(FACING, hit.side.opposite))
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
|
||||
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
|
||||
if (!entity.getStack().isEmpty && !player.isCreative) {
|
||||
ItemScatterer.spawn(world, pos, entity.inv)
|
||||
}
|
||||
super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
override fun getRenderType(state: BlockState?) = BlockRenderType.MODEL
|
||||
|
||||
override fun getComparatorOutput(state: BlockState, world: World, pos: BlockPos): Int {
|
||||
val stack = (world.getBlockEntity(pos) as DisplayCaseBlockEntity).getStack()
|
||||
|
||||
if (stack.isEmpty) return 0
|
||||
if (stack.item is PokeBallItem) return 3
|
||||
if (stack.item is BlockItem) return 2
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun hasComparatorOutput(state: BlockState?) = true
|
||||
|
||||
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
|
||||
|
||||
}
|
||||
@@ -62,8 +62,8 @@ class MonitorBlock(properties: Settings) : MultiblockBlock(properties) {
|
||||
if(world == null || pos == null) {
|
||||
return 0
|
||||
}
|
||||
val monitorEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
val multiBlockEntity = monitorEntity.multiblockStructure
|
||||
val monitorEntity = world.getBlockEntity(pos) as? MultiblockEntity
|
||||
val multiBlockEntity = monitorEntity?.multiblockStructure
|
||||
if(multiBlockEntity != null) {
|
||||
return multiBlockEntity.getComparatorOutput(state, world, pos)
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
@@ -148,8 +149,8 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
|
||||
if(world == null || pos == null) {
|
||||
return 0
|
||||
}
|
||||
val tankEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
val multiBlockEntity = tankEntity.multiblockStructure
|
||||
val tankEntity = world.getBlockEntity(pos) as? MultiblockEntity
|
||||
val multiBlockEntity = tankEntity?.multiblockStructure
|
||||
if(multiBlockEntity != null) {
|
||||
return multiBlockEntity.getComparatorOutput(state, world, pos)
|
||||
}
|
||||
@@ -179,7 +180,7 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
|
||||
return
|
||||
}
|
||||
val tankEntity = world.getBlockEntity(pos) as? MultiblockEntity
|
||||
tankEntity?.multiblockStructure!!.onTriggerEvent(state, world, pos, random)
|
||||
tankEntity?.multiblockStructure?.onTriggerEvent(state, world, pos, random)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(
|
||||
@@ -216,6 +217,11 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
|
||||
override fun asString() = label
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
companion object {
|
||||
val PART = EnumProperty.of("part", TankPart::class.java)
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
|
||||
@@ -9,15 +9,23 @@
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
@@ -28,7 +36,18 @@ class TumblestoneBlock(
|
||||
height: Int,
|
||||
xzOffset: Int,
|
||||
nextStage: Block?
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
|
||||
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage), Waterloggable {
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(FACING, Direction.DOWN)
|
||||
.with(WATERLOGGED, false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val WATERLOGGED = BooleanProperty.of("waterlogged")
|
||||
}
|
||||
|
||||
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
|
||||
if (stage == MAX_STAGE) return false
|
||||
val iterator: Iterator<BlockPos> =
|
||||
@@ -43,4 +62,31 @@ class TumblestoneBlock(
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState {
|
||||
return if (state.get(GildedChestBlock.WATERLOGGED)) {
|
||||
Fluids.WATER.getStill(false)
|
||||
} else super.getFluidState(state)
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
super.appendProperties(builder)
|
||||
builder.add(GildedChestBlock.WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
|
||||
return super.getPlacementState(blockPlaceContext)?.with(GildedChestBlock.WATERLOGGED, blockPlaceContext.world.getFluidState(blockPlaceContext.blockPos).fluid == Fluids.WATER)
|
||||
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState? {
|
||||
if (state.get(GildedChestBlock.WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
|
||||
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* 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.block.chest
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.block.entity.GildedChestBlockEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.toVec3d
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.mob.PiglinBrain
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.*
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWithEntity(settings), Waterloggable {
|
||||
|
||||
init {
|
||||
defaultState = defaultState
|
||||
.with(Properties.HORIZONTAL_FACING, Direction.SOUTH)
|
||||
.with(WATERLOGGED, false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val POKEMON_ARGS = "gimmighoul"
|
||||
val LEVEL_RANGE = 5..30
|
||||
val WATERLOGGED = BooleanProperty.of("waterlogged")
|
||||
|
||||
val SOUTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.25, 1.0, 1.0, 0.9375)
|
||||
)
|
||||
val NORTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0625, 1.0, 1.0, 0.75)
|
||||
)
|
||||
val WEST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0625, 0.0, 0.0, 0.75, 1.0, 1.0)
|
||||
)
|
||||
val EAST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.25, 0.0, 0.0, 0.9375, 1.0, 1.0)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState) = GildedChestBlockEntity(pos, state, type)
|
||||
|
||||
override fun getOutlineShape(
|
||||
state: BlockState,
|
||||
world: BlockView,
|
||||
pos: BlockPos,
|
||||
context: ShapeContext
|
||||
): VoxelShape {
|
||||
return when (state.get(HorizontalFacingBlock.FACING)) {
|
||||
Direction.NORTH -> NORTH_OUTLINE
|
||||
Direction.SOUTH -> SOUTH_OUTLINE
|
||||
Direction.WEST -> WEST_OUTLINE
|
||||
else -> EAST_OUTLINE
|
||||
}
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: WorldAccess,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
if (state.get(WATERLOGGED)) world.scheduleFluidTick(pos, Fluids.WATER, Fluids.WATER.getTickRate(world))
|
||||
return super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState {
|
||||
return if (state.get(WATERLOGGED)) {
|
||||
Fluids.WATER.getStill(false)
|
||||
} else super.getFluidState(state)
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
super.appendProperties(builder)
|
||||
builder.add(Properties.HORIZONTAL_FACING)
|
||||
builder.add(WATERLOGGED)
|
||||
}
|
||||
|
||||
private val facingToYaw: HashMap<Direction, Float> = hashMapOf(
|
||||
Direction.NORTH to -179.0F,
|
||||
Direction.WEST to 90.0F,
|
||||
Direction.SOUTH to 0.0F,
|
||||
Direction.EAST to -90.0F
|
||||
)
|
||||
|
||||
override fun getName(): MutableText {
|
||||
return if (isFake()) Text.translatable("block.cobblemon.gilded_chest") else super.getName()
|
||||
}
|
||||
|
||||
fun isFake() = (type == Type.FAKE)
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
|
||||
if (isFake()) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
spawnPokemon(world, pos, state, player)
|
||||
}
|
||||
world.setBlockState(pos, if (state.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState)
|
||||
} else super.onBreak(world, pos, state, player)
|
||||
}
|
||||
|
||||
private fun spawnPokemon(world: World, pos: BlockPos, state: BlockState, player: ServerPlayerEntity) : ActionResult {
|
||||
val properties = "$POKEMON_ARGS lvl=${LEVEL_RANGE.random()}"
|
||||
val pokemon = PokemonProperties.parse(properties)
|
||||
val entity = pokemon.createEntity(world)
|
||||
|
||||
// The yaw based on the block's facing direction
|
||||
val yaw = facingToYaw[state[HorizontalFacingBlock.FACING]] ?: 0.0F
|
||||
|
||||
entity.dataTracker.set(PokemonEntity.SPAWN_DIRECTION, facingToYaw[state[HorizontalFacingBlock.FACING]])
|
||||
val offsetDir = state[HorizontalFacingBlock.FACING]
|
||||
val vec = pos.toVec3d().add(offsetDir.offsetX * 0.1 + 0.5, 0.0, offsetDir.offsetZ * 0.1 + 0.5)
|
||||
entity.refreshPositionAndAngles(vec.x, vec.y, vec.z, yaw, entity.pitch)
|
||||
world.spawnEntity(entity)
|
||||
|
||||
world.removeBlock(pos, false)
|
||||
afterOnServer(ticks = 2) {
|
||||
if (player !in player.world.players) {
|
||||
return@afterOnServer
|
||||
}
|
||||
val party = player.party()
|
||||
if (!player.isCreative) {
|
||||
entity.forceBattle(player)
|
||||
} else {
|
||||
world.playSound(null, pos, CobblemonSounds.GIMMIGHOUL_REVEAL, SoundCategory.NEUTRAL)
|
||||
}
|
||||
}
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
if (isFake()) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
return spawnPokemon(world, pos, state, player)
|
||||
} else {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
val entity = world.getBlockEntity(pos) as? GildedChestBlockEntity ?: return ActionResult.FAIL
|
||||
if (world.getBlockState(pos.up()).isSolidBlock(world, pos.up())) return ActionResult.FAIL
|
||||
player.openHandledScreen(entity)
|
||||
if (!player.world.isClient) {
|
||||
PiglinBrain.onGuardedBlockInteracted(player, true)
|
||||
}
|
||||
val state = entity.poseableState
|
||||
state.currentModel?.let {
|
||||
it.moveToPose(null, state, it.getPose("OPEN")!!)
|
||||
}
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun onStateReplaced(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
newState: BlockState,
|
||||
moved: Boolean
|
||||
) {
|
||||
if (!state.isOf(newState.block)) {
|
||||
val chest = world.getBlockEntity(pos) as GildedChestBlockEntity
|
||||
ItemScatterer.spawn(world, pos, chest.inventoryContents)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRenderType(state: BlockState?) = BlockRenderType.ENTITYBLOCK_ANIMATED
|
||||
|
||||
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
|
||||
return defaultState
|
||||
.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing.opposite)
|
||||
.with(WATERLOGGED, blockPlaceContext.world.getFluidState(blockPlaceContext.blockPos).fluid == Fluids.WATER)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun rotate(state: BlockState, rotation: BlockRotation): BlockState {
|
||||
return state.with(
|
||||
Properties.HORIZONTAL_FACING, rotation.rotate(
|
||||
state.get(Properties.HORIZONTAL_FACING) as Direction
|
||||
)
|
||||
) as BlockState
|
||||
}
|
||||
|
||||
override fun hasComparatorOutput(state: BlockState?): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getComparatorOutput(state: BlockState?, world: World, pos: BlockPos?): Int {
|
||||
return ScreenHandler.calculateComparatorOutput(world.getBlockEntity(pos))
|
||||
}
|
||||
|
||||
override fun mirror(state: BlockState, mirror: BlockMirror): BlockState {
|
||||
return state.rotate(mirror.getRotation(state.get(Properties.HORIZONTAL_FACING) as Direction))
|
||||
}
|
||||
|
||||
enum class Type(val poserId: Identifier) {
|
||||
RED(cobblemonResource("gilded_chest")),
|
||||
BLUE(cobblemonResource("blue_gilded_chest")),
|
||||
GREEN(cobblemonResource("green_gilded_chest")),
|
||||
PINK(cobblemonResource("pink_gilded_chest")),
|
||||
WHITE(cobblemonResource("white_gilded_chest")),
|
||||
BLACK(cobblemonResource("black_gilded_chest")),
|
||||
YELLOW(cobblemonResource("yellow_gilded_chest")),
|
||||
FAKE(cobblemonResource("gilded_chest"))
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPlaced(
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
placer: LivingEntity?,
|
||||
itemStack: ItemStack
|
||||
) {
|
||||
val blockEntity = world.getBlockEntity(pos)
|
||||
if (itemStack.hasCustomName() && blockEntity is GildedChestBlockEntity) {
|
||||
blockEntity.customName = itemStack.name
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.chest
|
||||
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
class GildedState : PoseableEntityState<Entity>() {
|
||||
override fun getEntity() = null
|
||||
|
||||
init {
|
||||
setPose("CLOSED")
|
||||
}
|
||||
|
||||
override fun updatePartialTicks(partialTicks: Float) {
|
||||
this.currentPartialTicks += partialTicks
|
||||
}
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
}
|
||||
+180
@@ -0,0 +1,180 @@
|
||||
/*
|
||||
* 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.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.collection.DefaultedList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.World
|
||||
|
||||
class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISPLAY_CASE, pos, state), SidedInventory {
|
||||
|
||||
val inv: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY)
|
||||
|
||||
/**
|
||||
* Updates the [ItemStack] stored in this entity
|
||||
*
|
||||
* @param player The [PlayerEntity] that performed this interaction
|
||||
* @param hand The [Hand] the player used to perform this interaction
|
||||
* @author whatsy
|
||||
*/
|
||||
fun updateItem(player: PlayerEntity, hand: Hand): ActionResult {
|
||||
val playerStack = player.getStackInHand(hand)
|
||||
|
||||
// Player and case item are the same - do nothing
|
||||
if (playerStack.item == getStack().item) {
|
||||
return if (playerStack.item != Items.AIR) ActionResult.SUCCESS else ActionResult.FAIL
|
||||
}
|
||||
|
||||
// Player's hand is empty, case is not empty - give player the item in the case
|
||||
if (playerStack.isEmpty && !getStack().isEmpty) {
|
||||
if (!player.isCreative) player.setStackInHand(hand, getStack())
|
||||
setCaseStack(ItemStack.EMPTY)
|
||||
return ActionResult.success(true)
|
||||
}
|
||||
|
||||
// Case is empty, player's hand is not - put playerStack in the case
|
||||
if (getStack().isEmpty && !playerStack.isEmpty) {
|
||||
setCaseStack(playerStack.copy())
|
||||
if (!player.isCreative) playerStack.decrement(1)
|
||||
return ActionResult.success(true)
|
||||
}
|
||||
|
||||
// Player has item, case has item - swap items
|
||||
if (!getStack().isEmpty && !playerStack.isEmpty) {
|
||||
val oldCaseStack = getStack()
|
||||
setCaseStack(playerStack.copy())
|
||||
if (!player.isCreative) {
|
||||
playerStack.decrement(1)
|
||||
player.giveItemStack(oldCaseStack)
|
||||
}
|
||||
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
fun getStack(): ItemStack {
|
||||
return inv[0]
|
||||
}
|
||||
|
||||
private fun setCaseStack(newStack: ItemStack) {
|
||||
if (world == null) return
|
||||
val oldState = world!!.getBlockState(pos)
|
||||
newStack.count = 1
|
||||
inv[0] = newStack
|
||||
if (newStack.isEmpty) {
|
||||
world!!.playSound(null, pos, CobblemonSounds.DISPLAY_CASE_REMOVE_ITEM, SoundCategory.BLOCKS)
|
||||
} else {
|
||||
world!!.playSound(null, pos, CobblemonSounds.DISPLAY_CASE_ADD_ITEM, SoundCategory.BLOCKS)
|
||||
}
|
||||
world!!.updateListeners(pos, oldState, world!!.getBlockState(pos), Block.NOTIFY_LISTENERS)
|
||||
world!!.updateComparators(pos, world!!.getBlockState(pos).block)
|
||||
}
|
||||
|
||||
override fun writeNbt(nbt: NbtCompound) {
|
||||
super.writeNbt(nbt)
|
||||
Inventories.writeNbt(nbt, inv, true)
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound) {
|
||||
super.readNbt(nbt)
|
||||
inv.clear()
|
||||
Inventories.readNbt(nbt, inv)
|
||||
}
|
||||
|
||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
||||
return BlockEntityUpdateS2CPacket.create(this)
|
||||
}
|
||||
|
||||
override fun toInitialChunkDataNbt(): NbtCompound {
|
||||
return this.createNbt()
|
||||
}
|
||||
|
||||
private fun onItemUpdated(world: World, oldState: BlockState, newState: BlockState) {
|
||||
markDirty()
|
||||
world.updateListeners(pos, oldState, newState, Block.NOTIFY_LISTENERS)
|
||||
world.updateComparators(pos, world.getBlockState(pos).block)
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
inv.clear()
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return inv.size
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return getStack().isEmpty
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return getStack()
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
val oldState = cachedState
|
||||
val result = Inventories.splitStack(inv, slot, amount)
|
||||
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
|
||||
return result
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
val oldState = cachedState
|
||||
val result = Inventories.removeStack(inv, slot)
|
||||
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
|
||||
return result
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack) {
|
||||
val oldState = cachedState
|
||||
inv[slot] = stack
|
||||
if (stack.count > stack.maxCount) {
|
||||
stack.count = stack.maxCount
|
||||
}
|
||||
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?) = false
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray {
|
||||
val result = IntArray(inv.size)
|
||||
for (i in result.indices) {
|
||||
result[i] = i
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getMaxCountPerStack() = 1
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if(dir == Direction.DOWN) return false
|
||||
return getStack().isEmpty
|
||||
}
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean = (dir == Direction.DOWN)
|
||||
}
|
||||
+7
-2
@@ -19,6 +19,7 @@ import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtHelper
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.ChunkPos
|
||||
|
||||
open class FossilMultiblockEntity(
|
||||
pos: BlockPos,
|
||||
@@ -39,8 +40,12 @@ open class FossilMultiblockEntity(
|
||||
get() {
|
||||
return if (field != null) {
|
||||
field
|
||||
} else if (masterBlockPos != null && masterBlockPos != pos && world?.getBlockEntity(masterBlockPos) != null) {
|
||||
field = (world?.getBlockEntity(masterBlockPos) as FossilMultiblockEntity).multiblockStructure
|
||||
} else if (masterBlockPos != null && masterBlockPos != pos) {
|
||||
val chunkPos = ChunkPos(masterBlockPos)
|
||||
if (world?.chunkManager?.isChunkLoaded(chunkPos.x, chunkPos.z) == true) {
|
||||
val entity: FossilMultiblockEntity? = world?.getBlockEntity(masterBlockPos) as FossilMultiblockEntity?
|
||||
field = entity?.multiblockStructure
|
||||
}
|
||||
field
|
||||
} else {
|
||||
null
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock.Type
|
||||
import com.cobblemon.mod.common.block.chest.GildedState
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntityType
|
||||
import net.minecraft.block.entity.LootableContainerBlockEntity
|
||||
import net.minecraft.block.entity.ViewerCountManager
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.DoubleInventory
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.screen.GenericContainerScreenHandler
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.collection.DefaultedList
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.world.World
|
||||
|
||||
class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type = Type.RED) : LootableContainerBlockEntity(CobblemonBlockEntities.GILDED_CHEST, pos, state), SidedInventory {
|
||||
var inventoryContents: DefaultedList<ItemStack> = DefaultedList.ofSize(NUM_SLOTS, ItemStack.EMPTY)
|
||||
val poseableState: GildedState = GildedState()
|
||||
|
||||
private val stateManager: ViewerCountManager = object : ViewerCountManager() {
|
||||
override fun onContainerOpen(world: World, pos: BlockPos, state: BlockState) {
|
||||
playSound(world, pos, state, CobblemonSounds.GILDED_CHEST_OPEN)
|
||||
}
|
||||
|
||||
override fun onContainerClose(world: World, pos: BlockPos, state: BlockState) {
|
||||
playSound(world, pos, state, CobblemonSounds.GILDED_CHEST_CLOSE)
|
||||
}
|
||||
|
||||
override fun onViewerCountUpdate(
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
state: BlockState,
|
||||
oldViewerCount: Int,
|
||||
newViewerCount: Int
|
||||
) {
|
||||
this@GildedChestBlockEntity.onViewerCountUpdate(world, pos, state, oldViewerCount, newViewerCount)
|
||||
}
|
||||
|
||||
override fun isPlayerViewing(player: PlayerEntity): Boolean {
|
||||
if (player.currentScreenHandler is GenericContainerScreenHandler) {
|
||||
val inventory = (player.currentScreenHandler as GenericContainerScreenHandler).inventory
|
||||
return inventory === this@GildedChestBlockEntity || inventory is DoubleInventory && inventory.isPart(
|
||||
this@GildedChestBlockEntity
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType(): BlockEntityType<*> = CobblemonBlockEntities.GILDED_CHEST
|
||||
override fun size() = NUM_SLOTS
|
||||
|
||||
override fun getContainerName() = Text.translatable("block.cobblemon.gilded_chest")
|
||||
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?) =
|
||||
GenericContainerScreenHandler.createGeneric9x3(syncId, playerInventory, this)
|
||||
|
||||
override fun onOpen(player: PlayerEntity) {
|
||||
if (!this.removed && !player.isSpectator && type != Type.FAKE) {
|
||||
stateManager.openContainer(player, this.getWorld(), this.getPos(), this.cachedState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClose(player: PlayerEntity) {
|
||||
if (!this.removed && !player.isSpectator) {
|
||||
stateManager.closeContainer(player, this.getWorld(), this.getPos(), this.cachedState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getAvailableSlots(side: Direction): IntArray {
|
||||
return if (type == Type.FAKE) IntArray(0) else IntArray(NUM_SLOTS)
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if (type == Type.FAKE) return false
|
||||
return dir != Direction.DOWN
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
if (type == Type.FAKE) return false
|
||||
return dir == Direction.DOWN
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity) = !player.isSpectator
|
||||
|
||||
override fun getInvStackList(): DefaultedList<ItemStack> = inventoryContents
|
||||
|
||||
override fun setInvStackList(list: DefaultedList<ItemStack>) {
|
||||
inventoryContents = list
|
||||
}
|
||||
|
||||
companion object {
|
||||
val NUM_SLOTS = 27
|
||||
fun playSound(world: World, pos: BlockPos, state: BlockState, sound: net.minecraft.sound.SoundEvent) {
|
||||
var d = pos.x.toDouble() + 0.5
|
||||
val e = pos.y.toDouble() + 0.5
|
||||
var f = pos.z.toDouble() + 0.5
|
||||
val direction = state.get(Properties.HORIZONTAL_FACING)
|
||||
d += direction.offsetX.toDouble() * 0.5
|
||||
f += direction.offsetZ.toDouble() * 0.5
|
||||
world.playSound(
|
||||
null,
|
||||
d,
|
||||
e,
|
||||
f,
|
||||
sound,
|
||||
SoundCategory.BLOCKS,
|
||||
0.5f,
|
||||
world.random.nextFloat() * 0.1f + 0.9f
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSyncedBlockEvent(type: Int, data: Int): Boolean {
|
||||
if (type == 1) {
|
||||
val isNowOpen = data > 0
|
||||
val wasOpen = poseableState.currentPose == "OPEN"
|
||||
val model = poseableState.currentModel ?: return true
|
||||
if (isNowOpen && !wasOpen) {
|
||||
model.moveToPose(null, poseableState, model.getPose("OPEN")!!)
|
||||
} else if (!isNowOpen && wasOpen) {
|
||||
model.moveToPose(null, poseableState, model.getPose("CLOSED")!!)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return super.onSyncedBlockEvent(type, data)
|
||||
}
|
||||
|
||||
fun onViewerCountUpdate(world: World, pos: BlockPos, state: BlockState, oldViewerCount: Int, newViewerCount: Int) {
|
||||
val block = state.block
|
||||
world.addSyncedBlockEvent(pos, block, 1, newViewerCount)
|
||||
}
|
||||
override fun writeNbt(nbt: NbtCompound?) {
|
||||
super.writeNbt(nbt)
|
||||
if (!serializeLootTable(nbt)) {
|
||||
Inventories.writeNbt(nbt, inventoryContents)
|
||||
}
|
||||
}
|
||||
|
||||
override fun readNbt(nbt: NbtCompound?) {
|
||||
super.readNbt(nbt)
|
||||
inventoryContents= DefaultedList.ofSize(
|
||||
size(), ItemStack.EMPTY
|
||||
)
|
||||
if (!deserializeLootTable(nbt)) {
|
||||
Inventories.readNbt(nbt, inventoryContents)
|
||||
}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -140,7 +140,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, val state: BlockState) : BlockEnt
|
||||
if (fixedPosition != null) {
|
||||
entity.setPosition(fixedPosition.toCenterPos().subtract(0.0, 0.5, 0.0))
|
||||
val pc = Cobblemon.storage.getPC(player.uuid)
|
||||
entity.beamMode = 1
|
||||
entity.beamMode = 2
|
||||
afterOnServer(seconds = SendOutPokemonHandler.SEND_OUT_DURATION) {
|
||||
entity.beamMode = 0
|
||||
}
|
||||
|
||||
+1
-1
@@ -128,7 +128,7 @@ class RestorationTankBlockEntity(
|
||||
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
|
||||
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
|
||||
&& structure.createdPokemon == null
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return false
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
|
||||
if(canUtilize) {
|
||||
// See if there's room
|
||||
for (i in items.indices ) {
|
||||
|
||||
+12
-12
@@ -117,10 +117,10 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
||||
val monitorPos = monitorPositions[fossilTankIndex]
|
||||
val fossilAnalyzerPos = fossilAnalyzerPositions[fossilTankIndex]!!
|
||||
val restorationTankPos = restorationTankPositions[fossilTankIndex]!!
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
|
||||
val analyzerEntity = world.getBlockEntity(fossilAnalyzerPos) as MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(restorationTankPos) as MultiblockEntity
|
||||
val tankTopEntity = world.getBlockEntity(restorationTankPos.up()) as MultiblockEntity
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as? MultiblockEntity
|
||||
val analyzerEntity = world.getBlockEntity(fossilAnalyzerPos) as? MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(restorationTankPos) as? MultiblockEntity
|
||||
val tankTopEntity = world.getBlockEntity(restorationTankPos.up()) as? MultiblockEntity
|
||||
val structure = FossilMultiblockStructure(monitorPos, fossilAnalyzerPos, restorationTankPos)
|
||||
|
||||
structure.tankConnectorDirection = dirsToCheck.filter {
|
||||
@@ -128,20 +128,20 @@ class FossilMultiblockBuilder(val centerPos: BlockPos) : MultiblockStructureBuil
|
||||
return@filter adjPos == fossilAnalyzerPos
|
||||
}.first()
|
||||
|
||||
analyzerEntity.multiblockStructure = structure
|
||||
tankBaseEntity.multiblockStructure = structure
|
||||
tankTopEntity.multiblockStructure = structure
|
||||
monitorEntity.multiblockStructure = structure
|
||||
analyzerEntity?.multiblockStructure = structure
|
||||
tankBaseEntity?.multiblockStructure = structure
|
||||
tankTopEntity?.multiblockStructure = structure
|
||||
monitorEntity?.multiblockStructure = structure
|
||||
structure.syncToClient(world)
|
||||
structure.markDirty(world)
|
||||
|
||||
world.playSound(null, centerPos, CobblemonSounds.FOSSIL_MACHINE_ASSEMBLE, SoundCategory.BLOCKS)
|
||||
|
||||
//Set these to null so the builders can be freed
|
||||
analyzerEntity.multiblockBuilder = null
|
||||
tankBaseEntity.multiblockBuilder = null
|
||||
tankTopEntity.multiblockBuilder = null
|
||||
monitorEntity.multiblockBuilder = null
|
||||
analyzerEntity?.multiblockBuilder = null
|
||||
tankBaseEntity?.multiblockBuilder = null
|
||||
tankTopEntity?.multiblockBuilder = null
|
||||
monitorEntity?.multiblockBuilder = null
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
+89
-94
@@ -98,101 +98,97 @@ class FossilMultiblockStructure (
|
||||
): ActionResult {
|
||||
val stack = player.getStackInHand(interactionHand)
|
||||
|
||||
if (this.createdPokemon != null) {
|
||||
|
||||
if (!stack.isIn(CobblemonItemTags.POKE_BALLS) || stack.item !is PokeBallItem) {
|
||||
return ActionResult.PASS
|
||||
}
|
||||
|
||||
if(stack.isIn(CobblemonItemTags.POKE_BALLS) || stack.item is PokeBallItem) {
|
||||
if (player !is ServerPlayerEntity) {
|
||||
return ActionResult.CONSUME
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
if (this.createdPokemon != null) {
|
||||
|
||||
if (this.fossilOwnerUUID != null && player.uuid != this.fossilOwnerUUID) {
|
||||
var ownerName : String = "UNKNOWN_USER" // TODO: lang agnostic fallback
|
||||
server()?.userCache?.getByUuid(this.fossilOwnerUUID)?.orElse(null)?.name?.let {
|
||||
ownerName = it
|
||||
if (this.fossilOwnerUUID != null && player.uuid != this.fossilOwnerUUID) {
|
||||
var ownerName : String = "UNKNOWN_USER" // TODO: lang agnostic fallback
|
||||
server()?.userCache?.getByUuid(this.fossilOwnerUUID)?.orElse(null)?.name?.let {
|
||||
ownerName = it
|
||||
}
|
||||
player.sendMessage(lang("fossilmachine.protected", ownerName), true)
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
player.sendMessage(lang("fossilmachine.protected", ownerName), true)
|
||||
return ActionResult.FAIL
|
||||
|
||||
|
||||
|
||||
val ballType = (stack.item as PokeBallItem).pokeBall
|
||||
if (!player.isCreative) {
|
||||
stack?.decrement(1)
|
||||
}
|
||||
|
||||
this.createdPokemon!!.caughtBall = ballType
|
||||
player.party().add(this.createdPokemon!!)
|
||||
this.fossilState.growthState = "Taken"
|
||||
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, createdPokemon!!)
|
||||
|
||||
// Turn the monitor off
|
||||
val monitorState = world.getBlockState(monitorPos)
|
||||
if(!monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
|
||||
}
|
||||
|
||||
this.createdPokemon = null
|
||||
this.fossilOwnerUUID = null
|
||||
this.protectionTime = -1
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
|
||||
|
||||
val ballType = (stack.item as PokeBallItem).pokeBall
|
||||
if (!player.isCreative) {
|
||||
stack?.decrement(1)
|
||||
}
|
||||
|
||||
this.createdPokemon!!.caughtBall = ballType
|
||||
player.party().add(this.createdPokemon!!)
|
||||
this.fossilState.growthState = "Taken"
|
||||
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
CobblemonCriteria.RESURRECT_POKEMON.trigger(player, createdPokemon!!)
|
||||
|
||||
// Turn the monitor off
|
||||
val monitorState = world.getBlockState(monitorPos)
|
||||
if(!monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
|
||||
}
|
||||
|
||||
this.createdPokemon = null
|
||||
this.fossilOwnerUUID = null
|
||||
this.protectionTime = -1
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
if (this.isRunning()) {
|
||||
return ActionResult.PASS
|
||||
}
|
||||
|
||||
// Reclaim the last fossil from the machine if their hand is empty
|
||||
if (player.getStackInHand(interactionHand).isEmpty) {
|
||||
if (fossilInventory.isEmpty()) {
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
if(!this.isRunning() && this.createdPokemon == null) {
|
||||
if (fossilInventory.isEmpty()) {
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
player.setStackInHand(interactionHand, fossilInventory.last())
|
||||
|
||||
player.setStackInHand(interactionHand, fossilInventory.last())
|
||||
|
||||
// remove last fossil in the fossil machine stack when grabbed out of the machine
|
||||
this.fossilInventory.removeAt(fossilInventory.size - 1)
|
||||
if(!world.isClient) {
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
// remove last fossil in the fossil machine stack when grabbed out of the machine
|
||||
this.fossilInventory.removeAt(fossilInventory.size - 1)
|
||||
if(!world.isClient) {
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
}
|
||||
}
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
|
||||
// Check if the player is holding a fossil and if so insert it into the machine.
|
||||
if (Fossils.isFossilIngredient(stack)) {
|
||||
if (fossilInventory.size > Cobblemon.config.maxInsertedFossilItems) {
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
if(!this.isRunning() && this.createdPokemon == null) {
|
||||
if (fossilInventory.size > Cobblemon.config.maxInsertedFossilItems) {
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
val copyFossilStack = stack.copyWithCount(1)
|
||||
if (!player.isCreative) {
|
||||
stack?.decrement(1)
|
||||
}
|
||||
val copyFossilStack = stack.copyWithCount(1)
|
||||
if (!player.isCreative) {
|
||||
stack?.decrement(1)
|
||||
}
|
||||
|
||||
fossilOwnerUUID = player.uuid
|
||||
fossilInventory.add(copyFossilStack)
|
||||
if(!world.isClient) {
|
||||
this.updateFossilType(world)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
fossilOwnerUUID = player.uuid
|
||||
fossilInventory.add(copyFossilStack)
|
||||
if(!world.isClient) {
|
||||
this.updateFossilType(world)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.syncToClient(world)
|
||||
this.markDirty(world)
|
||||
}
|
||||
}
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
// Check if the player is holding a natural material and if so, feed it to the machine.
|
||||
if (NaturalMaterials.isNaturalMaterial(stack) && this.organicMaterialInside < MATERIAL_TO_START) {
|
||||
if (insertOrganicMaterial(stack, world)) {
|
||||
if (NaturalMaterials.isNaturalMaterial(stack)) {
|
||||
if (!this.isRunning() && this.createdPokemon == null && this.organicMaterialInside < MATERIAL_TO_START && insertOrganicMaterial(stack, world)) {
|
||||
this.lastInteraction = world.time
|
||||
if (!player.isCreative) {
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack)
|
||||
@@ -200,7 +196,6 @@ class FossilMultiblockStructure (
|
||||
player.giveOrDropItemStack(ItemStack(Registries.ITEM.get(returnItem)), false)
|
||||
}
|
||||
}
|
||||
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
|
||||
@@ -311,25 +306,25 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as MultiblockEntity
|
||||
val analyzerEntity = world.getBlockEntity(analyzerPos) as MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(tankBasePos) as MultiblockEntity
|
||||
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as MultiblockEntity
|
||||
val state = world.getBlockState(tankBaseEntity.pos)
|
||||
val monitorEntity = world.getBlockEntity(monitorPos) as? MultiblockEntity
|
||||
val analyzerEntity = world.getBlockEntity(analyzerPos) as? MultiblockEntity
|
||||
val tankBaseEntity = world.getBlockEntity(tankBasePos) as? MultiblockEntity
|
||||
val tankTopEntity = world.getBlockEntity(tankBasePos.up()) as? MultiblockEntity
|
||||
val state = world.getBlockState(tankBaseEntity?.pos)
|
||||
val direction = state.get(HorizontalFacingBlock.FACING).getOpposite()
|
||||
val wildPokemon: Pokemon? = this.createdPokemon
|
||||
|
||||
monitorEntity.multiblockStructure = null
|
||||
analyzerEntity.multiblockStructure = null
|
||||
tankBaseEntity.multiblockStructure = null
|
||||
tankTopEntity.multiblockStructure = null
|
||||
monitorEntity.masterBlockPos = null
|
||||
analyzerEntity.masterBlockPos = null
|
||||
tankBaseEntity.masterBlockPos = null
|
||||
tankTopEntity.masterBlockPos = null
|
||||
monitorEntity?.multiblockStructure = null
|
||||
analyzerEntity?.multiblockStructure = null
|
||||
tankBaseEntity?.multiblockStructure = null
|
||||
tankTopEntity?.multiblockStructure = null
|
||||
monitorEntity?.masterBlockPos = null
|
||||
analyzerEntity?.masterBlockPos = null
|
||||
tankBaseEntity?.masterBlockPos = null
|
||||
tankTopEntity?.masterBlockPos = null
|
||||
|
||||
// Drop fossils from machine as long as the machine is not running
|
||||
if (this.timeRemaining == TIME_TO_TAKE || this.timeRemaining == -1) {
|
||||
// Drop fossils from machine as long as the machine is not started or near completion
|
||||
if (this.timeRemaining == -1 || this.timeRemaining >= 20) {
|
||||
this.fossilInventory.forEach {
|
||||
val stack = ItemStack(it.item, 1)
|
||||
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), stack)
|
||||
@@ -544,7 +539,7 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
if (natureValue < 0 && organicMaterialInside == 0) return false
|
||||
val oldFillStage = organicMaterialInside / 8
|
||||
val oldFillStage = organicMaterialInside * 8 / MATERIAL_TO_START
|
||||
|
||||
// to prevent over/under filling the tank causing a crash
|
||||
if ((organicMaterialInside + natureValue) > MATERIAL_TO_START) {
|
||||
@@ -562,7 +557,7 @@ class FossilMultiblockStructure (
|
||||
world.playSound(null, this.tankBasePos, CobblemonSounds.FOSSIL_MACHINE_INSERT_DNA, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
}
|
||||
this.markDirty(world)
|
||||
if (oldFillStage != (organicMaterialInside / 8)) {
|
||||
if (oldFillStage != (organicMaterialInside * 8 / MATERIAL_TO_START)) {
|
||||
this.syncToClient(world)
|
||||
}
|
||||
return true
|
||||
@@ -624,10 +619,10 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
const val TICKS_PER_MINUTE = 1200
|
||||
const val MATERIAL_TO_START = 64
|
||||
const val TIME_TO_TAKE = TICKS_PER_MINUTE * 1
|
||||
const val MATERIAL_TO_START = 128
|
||||
const val TIME_TO_TAKE = TICKS_PER_MINUTE * 12
|
||||
const val TIME_PER_STAGE = TIME_TO_TAKE / 8
|
||||
const val PROTECTION_TIME = 6000
|
||||
const val PROTECTION_TIME = TICKS_PER_MINUTE * 5
|
||||
|
||||
fun fromNbt(nbt: NbtCompound, animAge: Int = -1, partialTicks: Float = 0f): FossilMultiblockStructure {
|
||||
val monitorPos = NbtHelper.toBlockPos(nbt.getCompound(DataKeys.MONITOR_POS))
|
||||
@@ -662,7 +657,7 @@ class FossilMultiblockStructure (
|
||||
if (nbt.contains(DataKeys.CREATED_POKEMON)) {
|
||||
result.createdPokemon = Pokemon.loadFromNBT(nbt.getCompound(DataKeys.CREATED_POKEMON))
|
||||
}
|
||||
result.fillLevel = result.organicMaterialInside / 8
|
||||
result.fillLevel = result.organicMaterialInside * 8 / MATERIAL_TO_START
|
||||
return result
|
||||
}
|
||||
|
||||
|
||||
@@ -25,13 +25,17 @@ import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
|
||||
import com.cobblemon.mod.common.client.render.block.*
|
||||
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
|
||||
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
|
||||
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
|
||||
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
|
||||
@@ -44,6 +48,7 @@ import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
@@ -115,7 +120,6 @@ object CobblemonClient {
|
||||
PlatformEvents.CLIENT_ITEM_TOOLTIP.subscribe { event ->
|
||||
val stack = event.stack
|
||||
val lines = event.lines
|
||||
val size = lines.size
|
||||
@Suppress("DEPRECATION")
|
||||
if (stack.item.registryEntry.key.isPresent && stack.item.registryEntry.key.get().value.namespace == Cobblemon.MODID) {
|
||||
if (stack.nbt?.getBoolean(DataKeys.HIDE_TOOLTIP) == true) {
|
||||
@@ -123,14 +127,14 @@ object CobblemonClient {
|
||||
}
|
||||
val language = Language.getInstance()
|
||||
val key = this.baseLangKeyForItem(stack)
|
||||
val offset = if (size > 1) 1 else 0
|
||||
val offset = if (lines.size > 1) 1 else 0
|
||||
if (language.hasTranslation(key)) {
|
||||
lines.add(size - offset, key.asTranslated().gray())
|
||||
lines.add(lines.size - offset, key.asTranslated().gray())
|
||||
}
|
||||
var i = 1
|
||||
var listKey = "${key}_$i"
|
||||
while(language.hasTranslation(listKey)) {
|
||||
lines.add(size - offset, listKey.asTranslated().gray())
|
||||
lines.add(lines.size - offset, listKey.asTranslated().gray())
|
||||
listKey = "${key}_${++i}"
|
||||
}
|
||||
}
|
||||
@@ -162,6 +166,7 @@ object CobblemonClient {
|
||||
|
||||
this.implementation.registerBlockRenderType(
|
||||
RenderLayer.getCutout(),
|
||||
CobblemonBlocks.GILDED_CHEST,
|
||||
CobblemonBlocks.FOSSIL_ANALYZER,
|
||||
CobblemonBlocks.APRICORN_DOOR,
|
||||
CobblemonBlocks.APRICORN_TRAPDOOR,
|
||||
@@ -215,6 +220,8 @@ object CobblemonClient {
|
||||
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST,
|
||||
CobblemonBlocks.DISPLAY_CASE
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -244,6 +251,8 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignBlockEntityRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -255,6 +264,8 @@ object CobblemonClient {
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
|
||||
LOGGER.info("Registering Boat with Chest renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
|
||||
LOGGER.info("Registering Generic Bedrock Entity renderer")
|
||||
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
|
||||
}
|
||||
|
||||
fun reloadCodedAssets(resourceManager: ResourceManager) {
|
||||
@@ -262,12 +273,14 @@ object CobblemonClient {
|
||||
BedrockParticleEffectRepository.loadEffects(resourceManager)
|
||||
BedrockAnimationRepository.loadAnimations(
|
||||
resourceManager = resourceManager,
|
||||
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories
|
||||
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories
|
||||
)
|
||||
PokemonModelRepository.reload(resourceManager)
|
||||
PokeBallModelRepository.reload(resourceManager)
|
||||
BerryModelRepository.reload(resourceManager)
|
||||
FossilModelRepository.reload(resourceManager)
|
||||
BlockEntityModelRepository.reload(resourceManager)
|
||||
GenericBedrockEntityModelRepository.reload(resourceManager)
|
||||
MiscModelRepository.reload(resourceManager)
|
||||
LOGGER.info("Loaded assets")
|
||||
}
|
||||
|
||||
+62
-38
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.entity
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.entity.PokemonSideDelegate
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
@@ -22,7 +21,6 @@ import com.cobblemon.mod.common.api.scheduling.lerpOnClient
|
||||
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
|
||||
@@ -34,11 +32,8 @@ import com.cobblemon.mod.common.util.MovingSoundInstance
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import java.lang.Float.min
|
||||
import kotlin.math.abs
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.entity.data.TrackedData
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvent
|
||||
import net.minecraft.util.Hand
|
||||
@@ -105,6 +100,8 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
this.addPrimaryAnimation(primaryAnimation)
|
||||
}
|
||||
} else if (data == PokemonEntity.BEAM_MODE) {
|
||||
// If you make adjustments to this code, make sure to Find Usages for both PokemonEntity.beamMode and PokemonEntity.BEAM_MODE
|
||||
// TODO: change beamMode to an enum, or a set of booleans (send-out vs recall, delay vs delay)
|
||||
val beamMode = currentEntity.beamMode
|
||||
when (beamMode) {
|
||||
0 -> { /* Do nothing */ }
|
||||
@@ -141,39 +138,37 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
currentEntity.after(seconds = POKEBALL_AIR_TIME){
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
ballDone = true
|
||||
if (client.soundManager.get(CobblemonSounds.POKE_BALL_OPEN.id) != null && !playedSendOutSound) {
|
||||
currentEntity.owner?.let {
|
||||
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
|
||||
playedSendOutSound = true
|
||||
/// create end rod particles in a 0.1 radius around the soundPos with a count of 50 and a random velocity of 0.1
|
||||
sendOutPosition?.let{
|
||||
val newPos = it.add(sendOutOffset)
|
||||
val ballType = currentEntity.pokemon.caughtBall.name.path.toLowerCase().replace("_","")
|
||||
val mode = if(currentEntity.isBattling) "battle" else "casual"
|
||||
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
|
||||
sendflash?.let { effect ->
|
||||
val wrapper = MatrixWrapper()
|
||||
val matrix = MatrixStack()
|
||||
matrix.translate(newPos.x, newPos.y, newPos.z)
|
||||
wrapper.updateMatrix(matrix.peek().positionMatrix)
|
||||
val world = MinecraftClient.getInstance().world ?: return@let
|
||||
if (client.soundManager.get(CobblemonSounds.POKE_BALL_SEND_OUT.id) != null && !playedSendOutSound) {
|
||||
client.world?.playSound(client.player, soundPos.x, soundPos.y, soundPos.z, SoundEvent.of(CobblemonSounds.POKE_BALL_SEND_OUT.id), SoundCategory.PLAYERS, 0.6f, 1f)
|
||||
playedSendOutSound = true
|
||||
}
|
||||
// Display ball-specific particles
|
||||
sendOutPosition?.let{
|
||||
val newPos = it.add(sendOutOffset)
|
||||
val ballType = currentEntity.pokemon.caughtBall.name.path.lowercase().replace("_","")
|
||||
val mode = if(currentEntity.isBattling) "battle" else "casual"
|
||||
val sendflash = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
|
||||
sendflash?.let { effect ->
|
||||
val wrapper = MatrixWrapper()
|
||||
val matrix = MatrixStack()
|
||||
matrix.translate(newPos.x, newPos.y, newPos.z)
|
||||
wrapper.updateMatrix(matrix.peek().positionMatrix)
|
||||
val world = MinecraftClient.getInstance().world ?: return@let
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
|
||||
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
|
||||
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
|
||||
afterOnClient(seconds = 0.01667f) {
|
||||
ballsparks?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
val ballsparks = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsparks"))
|
||||
val ballsendsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/${mode}/ballsendsparkle"))
|
||||
// using afterOnClient because it's such a small timeframe that it's unlikely the entity has been removed & we'd like the precision
|
||||
afterOnClient(seconds = 0.01667f) {
|
||||
ballsparks?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
ballsendsparkle?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
currentEntity.after(seconds = 0.4f) {
|
||||
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
|
||||
ballsparkle?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
}
|
||||
}
|
||||
ballsendsparkle?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
currentEntity.after(seconds = 0.4f) {
|
||||
val ballsparkle = BedrockParticleEffectRepository.getEffect(cobblemonResource("${ballType}/ballsparkle"))
|
||||
ballsparkle?.let { effect ->
|
||||
ParticleStorm(effect, wrapper, world).spawn()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -190,7 +185,35 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
2 -> {
|
||||
// Scaling up with no delay and no particles
|
||||
playedSendOutSound = false
|
||||
entityScaleModifier = 0F
|
||||
currentEntity.isInvisible = false
|
||||
val soundPos = currentEntity.pos
|
||||
val client = MinecraftClient.getInstance()
|
||||
|
||||
if (client.soundManager.get(CobblemonSounds.POKE_BALL_SEND_OUT.id) != null && !playedSendOutSound) {
|
||||
client.world?.playSound(
|
||||
client.player,
|
||||
soundPos.x,
|
||||
soundPos.y,
|
||||
soundPos.z,
|
||||
CobblemonSounds.POKE_BALL_SEND_OUT,
|
||||
SoundCategory.PLAYERS,
|
||||
0.6f,
|
||||
1f
|
||||
)
|
||||
playedSendOutSound = true
|
||||
}
|
||||
lerpOnClient(BEAM_SHRINK_TIME) { entityScaleModifier = it }
|
||||
currentEntity.after(seconds = BEAM_SHRINK_TIME * 2) {
|
||||
ballOffset = 0f
|
||||
ballRotOffset = 0f
|
||||
sendOutPosition = null
|
||||
}
|
||||
}
|
||||
3 -> {
|
||||
// Scaling down into pokeball
|
||||
entityScaleModifier = 1F
|
||||
beamStartTime = System.currentTimeMillis()
|
||||
@@ -203,6 +226,7 @@ class PokemonClientDelegate : PoseableEntityState<PokemonEntity>(), PokemonSideD
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> { /* Do nothing */ }
|
||||
}
|
||||
} else if (data == PokemonEntity.LABEL_LEVEL) {
|
||||
currentEntity.dataTracker.get(PokemonEntity.LABEL_LEVEL)
|
||||
|
||||
@@ -59,7 +59,7 @@ class PartyOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.ge
|
||||
BattleGUI::class.java
|
||||
)
|
||||
|
||||
private val starterToast = CobblemonToast(
|
||||
val starterToast = CobblemonToast(
|
||||
MathHelper.randomUuid(),
|
||||
CobblemonItems.POKE_BALL.defaultStack,
|
||||
lang("ui.starter.choose_starter_title", SummaryBinding.boundKey().localizedText).red(),
|
||||
@@ -104,24 +104,12 @@ class PartyOverlay : InGameHud(MinecraftClient.getInstance(), MinecraftClient.ge
|
||||
!CobblemonClient.checkedStarterScreen
|
||||
) {
|
||||
if (!this.attachedToast) {
|
||||
// Adding starter toast on start and connect of client to server.
|
||||
minecraft.toastManager.add(this.starterToast)
|
||||
this.attachedToast = true
|
||||
}
|
||||
/*
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = lang("ui.starter.chooseyourstarter", SummaryBinding.boundKey().localizedText),
|
||||
x = minecraft.window.scaledWidth / 2,
|
||||
y = 70,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
*/
|
||||
}
|
||||
return
|
||||
}
|
||||
if (this.starterToast.nextVisibility != Toast.Visibility.HIDE) {
|
||||
this.starterToast.nextVisibility = Toast.Visibility.HIDE
|
||||
return // Don't render the stuff below if no Pokémon in party
|
||||
}
|
||||
|
||||
val totalHeight = party.slots.size * SLOT_HEIGHT
|
||||
|
||||
+2
-1
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.battle.SingleActionRequest
|
||||
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
|
||||
@@ -21,7 +23,6 @@ import net.minecraft.client.gui.Selectable
|
||||
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.Identifier
|
||||
class BattleGeneralActionSelection(
|
||||
|
||||
+39
-36
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.gui.ColourLibrary
|
||||
import com.cobblemon.mod.common.api.gui.MultiLineLabelK
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.startselection.widgets.CategoryList
|
||||
import com.cobblemon.mod.common.client.gui.startselection.widgets.ExitButton
|
||||
@@ -26,7 +27,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.type.DualTypeWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.type.SingleTypeWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.type.TypeWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.net.messages.server.SelectStarterPacket
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
@@ -37,8 +38,9 @@ import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.client.toast.Toast
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Starterselection Screen Thingy
|
||||
@@ -46,7 +48,7 @@ import net.minecraft.text.Text
|
||||
* @author Qu
|
||||
* @since 2022-06-18
|
||||
*/
|
||||
class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter.title".asTranslated()) {
|
||||
class StarterSelectionScreen(categories: Map<Identifier, RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
|
||||
|
||||
companion object {
|
||||
// Size of UI at scale 1
|
||||
@@ -63,25 +65,24 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
private val doubleTypeBackground = cobblemonResource("textures/gui/starterselection/starterselection_type_slot2.png")
|
||||
}
|
||||
|
||||
lateinit var categories: List<RenderableStarterCategory>
|
||||
lateinit var currentCategory: RenderableStarterCategory
|
||||
lateinit var modelWidget: ModelWidget
|
||||
lateinit var currentPokemon: RenderablePokemon
|
||||
var currentSelection = 0
|
||||
lateinit var rightButton: ArrowButton
|
||||
lateinit var leftButton: ArrowButton
|
||||
lateinit var typeWidget: TypeWidget
|
||||
lateinit var selectionButton: SelectionButton
|
||||
lateinit var starterRoundaboutCenter: StarterRoundabout
|
||||
lateinit var starterRoundaboutLeft: StarterRoundabout
|
||||
lateinit var starterRoundaboutRight: StarterRoundabout
|
||||
|
||||
constructor(categories: List<RenderableStarterCategory>) : this() {
|
||||
this.categories = categories
|
||||
}
|
||||
private val categories = categories.toSortedMap { a, b -> a.compareTo(b) }
|
||||
private var currentSelection = 0
|
||||
private lateinit var currentCategoryId: Identifier
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private lateinit var currentPokemon: RenderablePokemon
|
||||
private lateinit var typeWidget: TypeWidget
|
||||
private lateinit var starterRoundaboutCenter: StarterRoundabout
|
||||
private lateinit var starterRoundaboutLeft: StarterRoundabout
|
||||
private lateinit var starterRoundaboutRight: StarterRoundabout
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
// Hide toast once checkedStarterScreen was set, which happens during the opening of the starter screen.
|
||||
if (CobblemonClient.checkedStarterScreen) {
|
||||
if (CobblemonClient.overlay.starterToast.nextVisibility != Toast.Visibility.HIDE) {
|
||||
CobblemonClient.overlay.starterToast.nextVisibility = Toast.Visibility.HIDE
|
||||
}
|
||||
}
|
||||
|
||||
val x = (width - BASE_WIDTH) / 2
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
@@ -96,13 +97,13 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
paneWidth = 71, paneHeight = BASE_HEIGHT - 11,
|
||||
topOffset = 6, bottomOffset = 5,
|
||||
entryHeight = 20, entryWidth = 57,
|
||||
categories = categories,
|
||||
categories = this.categories,
|
||||
x = x - 2, y = y + 8,
|
||||
starterSelectionScreen = this
|
||||
)
|
||||
)
|
||||
|
||||
rightButton = ArrowButton(
|
||||
val rightButton = ArrowButton(
|
||||
pX = x + 183, pY = y + 151,
|
||||
pWidth = 9, pHeight = 14,
|
||||
right = true
|
||||
@@ -110,7 +111,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
right()
|
||||
}
|
||||
|
||||
leftButton = ArrowButton(
|
||||
val leftButton = ArrowButton(
|
||||
pX = x + 72, pY = y + 151,
|
||||
pWidth = 9, pHeight = 14,
|
||||
right = false
|
||||
@@ -121,8 +122,8 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
addDrawableChild(rightButton)
|
||||
addDrawableChild(leftButton)
|
||||
|
||||
currentCategory = categories.first()
|
||||
currentPokemon = currentCategory.pokemon[currentSelection]
|
||||
currentCategoryId = categories.firstKey()
|
||||
currentPokemon = this.currentCategory().pokemon[currentSelection]
|
||||
|
||||
with(currentPokemon) {
|
||||
modelWidget = ModelWidget(
|
||||
@@ -137,17 +138,17 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
|
||||
addDrawableChild(modelWidget)
|
||||
|
||||
selectionButton = SelectionButton(
|
||||
val selectionButton = SelectionButton(
|
||||
pX = x + 106, pY = y + 124,
|
||||
pWidth = SelectionButton.BUTTON_WIDTH, pHeight = SelectionButton.BUTTON_HEIGHT
|
||||
) {
|
||||
CobblemonNetwork.sendPacketToServer(
|
||||
SelectStarterPacket(
|
||||
categoryName = currentCategory.name,
|
||||
categoryId = currentCategoryId,
|
||||
selected = currentSelection
|
||||
)
|
||||
)
|
||||
MinecraftClient.getInstance().setScreen(null)
|
||||
this.close()
|
||||
}
|
||||
|
||||
addDrawableChild(selectionButton)
|
||||
@@ -162,7 +163,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
starterRoundaboutLeft = StarterRoundabout(
|
||||
pX = x + 89, pY = height / 2 + 84,
|
||||
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
|
||||
pokemon = currentCategory.pokemon[leftOfCurrentSelection()],
|
||||
pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()],
|
||||
clickAction = { _, _ -> this.left() },
|
||||
rotationVector = this.modelWidget.rotVec
|
||||
)
|
||||
@@ -170,7 +171,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
starterRoundaboutRight = StarterRoundabout(
|
||||
pX = x + 149, pY = height / 2 + 84,
|
||||
pWidth = StarterRoundabout.MODEL_WIDTH, pHeight = StarterRoundabout.MODEL_HEIGHT,
|
||||
pokemon = currentCategory.pokemon[rightOfCurrentSelection()],
|
||||
pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()],
|
||||
clickAction = { _, _ -> this.right() },
|
||||
rotationVector = this.modelWidget.rotVec
|
||||
)
|
||||
@@ -277,8 +278,8 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
fun changeCategory(category: RenderableStarterCategory) {
|
||||
currentCategory = category
|
||||
fun changeCategory(id: Identifier) {
|
||||
currentCategoryId = id
|
||||
currentSelection = 0
|
||||
updateSelection()
|
||||
}
|
||||
@@ -289,7 +290,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
updateSelection()
|
||||
}
|
||||
|
||||
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= currentCategory.pokemon.size - 1) currentSelection + 1 else 0
|
||||
private fun rightOfCurrentSelection() : Int = if (currentSelection + 1 <= this.currentCategory().pokemon.size - 1) currentSelection + 1 else 0
|
||||
|
||||
private fun left() {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
@@ -297,16 +298,16 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
updateSelection()
|
||||
}
|
||||
|
||||
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) currentCategory.pokemon.size - 1 else currentSelection - 1
|
||||
private fun leftOfCurrentSelection() : Int = if (currentSelection - 1 == -1) this.currentCategory().pokemon.size - 1 else currentSelection - 1
|
||||
|
||||
private fun updateSelection() {
|
||||
currentPokemon = currentCategory.pokemon[currentSelection].also {
|
||||
currentPokemon = this.currentCategory().pokemon[currentSelection].also {
|
||||
modelWidget.pokemon = it
|
||||
typeWidget = typeWidget(it, (width - BASE_WIDTH) / 2, (height - BASE_HEIGHT) / 2)
|
||||
}
|
||||
starterRoundaboutLeft.pokemon = currentCategory.pokemon[leftOfCurrentSelection()]
|
||||
starterRoundaboutLeft.pokemon = this.currentCategory().pokemon[leftOfCurrentSelection()]
|
||||
starterRoundaboutCenter.pokemon = currentPokemon
|
||||
starterRoundaboutRight.pokemon = currentCategory.pokemon[rightOfCurrentSelection()]
|
||||
starterRoundaboutRight.pokemon = this.currentCategory().pokemon[rightOfCurrentSelection()]
|
||||
}
|
||||
|
||||
private fun typeWidget(pokemon: RenderablePokemon, x: Int, y: Int) : TypeWidget {
|
||||
@@ -325,5 +326,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
|
||||
)
|
||||
}
|
||||
|
||||
private fun currentCategory(): RenderableStarterCategory = this.categories[this.currentCategoryId]!!
|
||||
|
||||
override fun shouldPause() = true
|
||||
}
|
||||
+9
-19
@@ -12,14 +12,14 @@ import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.client.gui.startselection.StarterSelectionScreen
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.config.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.api.starter.RenderableStarterCategory
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class CategoryList(
|
||||
private val paneWidth: Int,
|
||||
@@ -28,7 +28,7 @@ class CategoryList(
|
||||
bottomOffset: Int,
|
||||
private val entryWidth: Int,
|
||||
entryHeight: Int,
|
||||
private val categories: List<RenderableStarterCategory>,
|
||||
private val categories: Map<Identifier, RenderableStarterCategory>,
|
||||
val x: Int, val y: Int,
|
||||
private val minecraft: MinecraftClient = MinecraftClient.getInstance(),
|
||||
private val starterSelectionScreen: StarterSelectionScreen
|
||||
@@ -44,7 +44,6 @@ class CategoryList(
|
||||
companion object {
|
||||
private const val CATEGORY_BUTTON_WIDTH = 51.5f
|
||||
private const val CATEGORY_BUTTON_HEIGHT = 16f
|
||||
private const val ENTRY_X_OFFSET = 10f
|
||||
private val categoryResource = cobblemonResource("textures/gui/starterselection/starterselection_slot.png")
|
||||
}
|
||||
|
||||
@@ -53,19 +52,12 @@ class CategoryList(
|
||||
this.setRenderHorizontalShadows(false)
|
||||
this.setRenderBackground(false)
|
||||
this.setRenderSelection(false)
|
||||
}
|
||||
|
||||
private var entriesCreated = false
|
||||
|
||||
private fun createEntries() = categories.map {
|
||||
Category(it)
|
||||
this.categories.forEach { (id, category) ->
|
||||
this.addEntry(Category(id, category))
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (!entriesCreated) {
|
||||
createEntries().forEach { addEntry(it) }
|
||||
entriesCreated = true
|
||||
}
|
||||
context.enableScissor(
|
||||
x,
|
||||
y,
|
||||
@@ -80,15 +72,13 @@ class CategoryList(
|
||||
this.updateSize(this.paneWidth, this.paneHeight, this.y, this.y + this.paneHeight)
|
||||
this.setLeftPos(this.x)
|
||||
}
|
||||
|
||||
private fun scale(n: Int): Int = (this.client.window.scaleFactor * n).toInt()
|
||||
override fun getRowWidth() = this.entryWidth
|
||||
override fun getScrollbarPositionX(): Int {
|
||||
return this.left + this.width - 5
|
||||
}
|
||||
|
||||
|
||||
inner class Category(private val category: RenderableStarterCategory) : Entry<Category>() {
|
||||
inner class Category(private val id: Identifier, private val category: RenderableStarterCategory) : Entry<Category>() {
|
||||
|
||||
override fun render(
|
||||
context: DrawContext,
|
||||
@@ -121,7 +111,7 @@ class CategoryList(
|
||||
)
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = category.displayNameText,
|
||||
text = category.displayName.copy(),
|
||||
x = x + 28,
|
||||
y = y + 4.5F,
|
||||
scale = 1F,
|
||||
@@ -132,7 +122,7 @@ class CategoryList(
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
starterSelectionScreen.changeCategory(category = category)
|
||||
starterSelectionScreen.changeCategory(this.id)
|
||||
minecraft.soundManager.play(PositionedSoundInstance.master(CobblemonSounds.GUI_CLICK, 1.0F))
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.api.moves.MoveSet
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies.species
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.emitWhile
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
@@ -33,6 +35,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MoveSwa
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.moves.MovesWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.MovePartyPokemonPacket
|
||||
import com.cobblemon.mod.common.net.messages.server.storage.party.SwapPartyPokemonPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
@@ -59,7 +62,7 @@ import net.minecraft.text.Text
|
||||
*
|
||||
* @param selection The index the [party] will have as the base [selectedPokemon].
|
||||
*/
|
||||
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, selection: Int): Screen(Text.translatable("cobblemon.ui.summary.title")), Schedulable {
|
||||
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, private val selection: Int): Screen(Text.translatable("cobblemon.ui.summary.title")), Schedulable {
|
||||
|
||||
companion object {
|
||||
const val BASE_WIDTH = 331
|
||||
@@ -106,24 +109,27 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
|
||||
internal var selectedPokemon: Pokemon
|
||||
internal lateinit var selectedPokemon: Pokemon
|
||||
private lateinit var mainScreen: ClickableWidget
|
||||
lateinit var sideScreen: Element
|
||||
lateinit var modelWidget: ModelWidget
|
||||
lateinit var nicknameEntryWidget: NicknameEntryWidget
|
||||
private lateinit var modelWidget: ModelWidget
|
||||
private lateinit var nicknameEntryWidget: NicknameEntryWidget
|
||||
private val summaryTabs = mutableListOf<SummaryTab>()
|
||||
private var mainScreenIndex = INFO
|
||||
var sideScreenIndex = PARTY
|
||||
private val party = ArrayList(party)
|
||||
|
||||
init {
|
||||
/**
|
||||
* Initializes the Summary Screen
|
||||
*/
|
||||
override fun init() {
|
||||
super.init()
|
||||
if (this.party.isEmpty()) {
|
||||
throw IllegalArgumentException("Summary UI cannot display zero Pokemon")
|
||||
}
|
||||
if (this.party.size > 6) {
|
||||
throw IllegalArgumentException("Summary UI cannot display more than six Pokemon")
|
||||
}
|
||||
|
||||
val idealSelected = this.party[selection]
|
||||
if (idealSelected == null) {
|
||||
this.selectedPokemon = this.party.first { it != null }!!
|
||||
@@ -131,14 +137,6 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
this.selectedPokemon = idealSelected
|
||||
}
|
||||
this.listenToMoveSet()
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the Summary Screen
|
||||
*/
|
||||
public override fun init() {
|
||||
clearChildren()
|
||||
super.init()
|
||||
|
||||
val x = (width - BASE_WIDTH) / 2
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
@@ -245,6 +243,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
rotationY = 325F,
|
||||
offsetY = -10.0
|
||||
)
|
||||
addDrawable(this.modelWidget)
|
||||
}
|
||||
|
||||
fun swapPartySlot(sourceIndex: Int, targetIndex: Int) {
|
||||
@@ -281,8 +280,12 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
listenToMoveSet()
|
||||
displayMainScreen(mainScreenIndex)
|
||||
children().find { it is EvolutionSelectScreen }?.let(this::remove)
|
||||
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
|
||||
nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
|
||||
if (this::modelWidget.isInitialized) {
|
||||
this.modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
|
||||
}
|
||||
if (this::nicknameEntryWidget.isInitialized) {
|
||||
this.nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
|
||||
}
|
||||
}
|
||||
|
||||
private var moveSetSubscription: ObservableSubscription<MoveSet>? = null
|
||||
@@ -309,7 +312,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
*/
|
||||
private fun displayMainScreen(screen: Int) {
|
||||
// Get stat tab index if currently displaying stat screen
|
||||
var subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
|
||||
val subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
|
||||
|
||||
mainScreenIndex = screen
|
||||
if (::mainScreen.isInitialized) remove(mainScreen)
|
||||
@@ -408,10 +411,10 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
renderBackground(context)
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
//this.renderBackground(context)
|
||||
|
||||
schedulingTracker.update(pPartialTicks / 20F)
|
||||
schedulingTracker.update(delta / 20F)
|
||||
|
||||
val x = (width - BASE_WIDTH) / 2
|
||||
val y = (height - BASE_HEIGHT) / 2
|
||||
@@ -427,7 +430,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
height = PORTRAIT_SIZE
|
||||
)
|
||||
|
||||
modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
//modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
|
||||
// Render Base Resource
|
||||
blitk(
|
||||
@@ -576,29 +579,29 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
)
|
||||
|
||||
// Render all added Widgets
|
||||
super.render(context, pMouseX, pMouseY, pPartialTicks)
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
|
||||
// Render Item Tooltip
|
||||
if (!heldItem.isEmpty) {
|
||||
val itemHovered = pMouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && pMouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.drawItemTooltip(MinecraftClient.getInstance().textRenderer, heldItem, pMouseX, pMouseY)
|
||||
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
|
||||
if (itemHovered) context.drawItemTooltip(MinecraftClient.getInstance().textRenderer, heldItem, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether this Screen should pause the Game in SinglePlayer
|
||||
*/
|
||||
override fun shouldPause(): Boolean {
|
||||
return false
|
||||
}
|
||||
override fun shouldPause(): Boolean = false
|
||||
|
||||
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
|
||||
return children().any { it.mouseScrolled(mouseX, mouseY, amount) }
|
||||
}
|
||||
|
||||
/*
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
return children().any { it.mouseClicked(mouseX, mouseY, button) }
|
||||
}
|
||||
*/
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
@@ -606,17 +609,48 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
}
|
||||
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
if ((keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_KP_ENTER) && nicknameEntryWidget.isFocused) { // Enter pressed
|
||||
nicknameEntryWidget.isFocused = false
|
||||
if ((keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_KP_ENTER)
|
||||
&& this::nicknameEntryWidget.isInitialized
|
||||
&& this.nicknameEntryWidget.isFocused
|
||||
) {
|
||||
this.focused = null
|
||||
}
|
||||
return nicknameEntryWidget.keyPressed(keyCode, scanCode, modifiers) || super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
override fun charTyped(chr: Char, modifiers: Int): Boolean {
|
||||
return nicknameEntryWidget.charTyped(chr, modifiers) || super.charTyped(chr, modifiers)
|
||||
if (Cobblemon.config.enableDebugKeys) {
|
||||
val model = PokemonModelRepository.getPoser(selectedPokemon.species.resourceIdentifier, selectedPokemon.aspects)
|
||||
if (keyCode == InputUtil.GLFW_KEY_UP) {
|
||||
model.profileTranslation = model.profileTranslation.add(0.0, -0.01, 0.0)
|
||||
}
|
||||
if (keyCode == InputUtil.GLFW_KEY_DOWN) {
|
||||
model.profileTranslation = model.profileTranslation.add(0.0, 0.01, 0.0)
|
||||
}
|
||||
if (keyCode == InputUtil.GLFW_KEY_LEFT) {
|
||||
model.profileTranslation = model.profileTranslation.add(-0.01, 0.0, 0.0)
|
||||
}
|
||||
if (keyCode == InputUtil.GLFW_KEY_RIGHT) {
|
||||
model.profileTranslation = model.profileTranslation.add(0.01, 0.0, 0.0)
|
||||
}
|
||||
if (keyCode == InputUtil.GLFW_KEY_EQUAL) {
|
||||
model.profileScale += 0.01F
|
||||
}
|
||||
if (keyCode == InputUtil.GLFW_KEY_MINUS) {
|
||||
model.profileScale -= 0.01F
|
||||
}
|
||||
}
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
fun playSound(soundEvent: SoundEvent) {
|
||||
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
if (Cobblemon.config.enableDebugKeys) {
|
||||
val model = PokemonModelRepository.getPoser(selectedPokemon.species.resourceIdentifier, selectedPokemon.aspects)
|
||||
MinecraftClient.getInstance().player?.sendMessage(Text.of("Profile Translation: ${model.profileTranslation}"))
|
||||
MinecraftClient.getInstance().player?.sendMessage(Text.of("Profile Scale: ${model.profileScale}"))
|
||||
Cobblemon.LOGGER.info("override var profileTranslation = Vec3d(${model.profileTranslation.x}, ${model.profileTranslation.y}, ${model.profileTranslation.z})")
|
||||
Cobblemon.LOGGER.info("override var profileScale = ${model.profileScale}F")
|
||||
}
|
||||
super.close()
|
||||
}
|
||||
}
|
||||
|
||||
+12
-13
@@ -19,6 +19,7 @@ import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class SummaryButton(
|
||||
@@ -27,7 +28,7 @@ class SummaryButton(
|
||||
val buttonWidth: Number,
|
||||
val buttonHeight: Number,
|
||||
val clickAction: PressAction,
|
||||
private val text: MutableText? = null,
|
||||
private val text: MutableText = Text.empty(),
|
||||
private val resource: Identifier,
|
||||
private val activeResource: Identifier? = null,
|
||||
private val renderRequirement: ((button: SummaryButton) -> Boolean) = { true },
|
||||
@@ -68,18 +69,16 @@ class SummaryButton(
|
||||
)
|
||||
|
||||
// Render Text
|
||||
if (text != null) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = if (largeText) CobblemonResources.DEFAULT_LARGE else null,
|
||||
text = if (boldText) text.bold() else text,
|
||||
x = buttonX + (buttonWidth.toFloat() / 2),
|
||||
y = buttonY + (buttonHeight.toFloat() / 2) - ((TEXT_HEIGHT / 2) * textScale),
|
||||
scale = textScale,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = if (largeText) CobblemonResources.DEFAULT_LARGE else null,
|
||||
text = if (boldText) text.bold() else text,
|
||||
x = buttonX + (buttonWidth.toFloat() / 2),
|
||||
y = buttonY + (buttonHeight.toFloat() / 2) - ((TEXT_HEIGHT / 2) * textScale),
|
||||
scale = textScale,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
/*
|
||||
* 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.client.gui.summary.featurerenderers
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
* Renders an [IntSpeciesFeature] as a bar in the summary screen.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
class BarSummarySpeciesFeatureRenderer(
|
||||
override val name: String,
|
||||
val displayName: MutableText,
|
||||
val min: Int,
|
||||
val max: Int,
|
||||
val colour: Vec3d,
|
||||
val underlay: Identifier,
|
||||
val overlay: Identifier,
|
||||
val pokemon: Pokemon
|
||||
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
|
||||
override fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
|
||||
val value = feature.value
|
||||
val barRatio = (value - min) / (max - min).toFloat()
|
||||
val barWidth = MathHelper.ceil(barRatio * 108)
|
||||
|
||||
blitk(
|
||||
matrixStack = drawContext.matrices,
|
||||
texture = underlay,
|
||||
x = x,
|
||||
y = y,
|
||||
height = 28,
|
||||
width = 124
|
||||
)
|
||||
|
||||
val red = colour.x / 255F
|
||||
val green = colour.y / 255F
|
||||
val blue = colour.z / 255F
|
||||
|
||||
blitk(
|
||||
matrixStack = drawContext.matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 8,
|
||||
y = y + 16,
|
||||
height = 10,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = drawContext.matrices,
|
||||
texture = overlay,
|
||||
x = x / StatWidget.SCALE,
|
||||
y = (y + 16) / StatWidget.SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = StatWidget.SCALE
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = drawContext,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = displayName.bold(),
|
||||
x = x + 62,
|
||||
y = y + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = drawContext,
|
||||
text = value.toString().text(),
|
||||
x = x + 11,
|
||||
y = y + 6,
|
||||
scale = StatWidget.SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = drawContext,
|
||||
text = "${MathHelper.floor(barRatio * 100)}%".text(),
|
||||
x = x + 113,
|
||||
y = y + 6,
|
||||
scale = StatWidget.SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.summary.featurerenderers
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeature
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
|
||||
/**
|
||||
* A renderer for some kind of [SynchronizedSpeciesFeature] so that it will display
|
||||
* in the summary screen of the Pokémon (under other stats).
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since November 13th, 2023
|
||||
*/
|
||||
interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
|
||||
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
|
||||
val name: String
|
||||
/** Draws it at a particular position. */
|
||||
fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon, feature: T)
|
||||
fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon): Boolean {
|
||||
val feature = pokemon.getFeature<T>(name) ?: return false
|
||||
render(drawContext, x, y, pokemon, feature)
|
||||
return true
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.summary.widgets
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.summary.Summary
|
||||
@@ -23,15 +22,14 @@ import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class PartySlotWidget(
|
||||
private val pX: Number,
|
||||
private val pY: Number,
|
||||
pX: Number,
|
||||
pY: Number,
|
||||
private val partyWidget: PartyWidget,
|
||||
private val summary: Summary,
|
||||
private val pokemon: Pokemon?,
|
||||
|
||||
-2
@@ -26,9 +26,7 @@ class InfoBlockWidget(
|
||||
private val font: Identifier,
|
||||
) : SoundlessWidget(pX, pY, blockWidth, blockHeight, Text.literal("InfoBlockWidget")) {
|
||||
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = font,
|
||||
|
||||
+1
-2
@@ -15,14 +15,13 @@ import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.widget.ButtonWidget
|
||||
import net.minecraft.client.sound.PositionedSoundInstance
|
||||
import net.minecraft.client.sound.SoundManager
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.text.Text
|
||||
|
||||
class ReorderMoveButton(
|
||||
val pX: Int, val pY: Int,
|
||||
private val isUp: Boolean,
|
||||
onPress: PressAction
|
||||
): ButtonWidget((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
): ButtonWidget((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.empty(), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
companion object {
|
||||
private const val WIDTH = 8
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ class SwapMoveButton(
|
||||
var move: MoveTemplate,
|
||||
var movesWidget: MovesWidget,
|
||||
onPress: PressAction
|
||||
): ButtonWidget((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
): ButtonWidget((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.empty(), onPress, DEFAULT_NARRATION_SUPPLIER) {
|
||||
|
||||
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double): Boolean {
|
||||
return false
|
||||
|
||||
+94
-51
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
@@ -54,7 +56,7 @@ class StatWidget(
|
||||
|
||||
private const val WIDTH = 134
|
||||
private const val HEIGHT = 148
|
||||
private const val SCALE = 0.5F
|
||||
const val SCALE = 0.5F
|
||||
|
||||
private const val WHITE = 0x00FFFFFF
|
||||
private const val GREY = 0x00AAAAAA
|
||||
@@ -64,6 +66,7 @@ class StatWidget(
|
||||
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
|
||||
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
|
||||
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
|
||||
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
|
||||
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
|
||||
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
|
||||
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
|
||||
@@ -84,6 +87,10 @@ class StatWidget(
|
||||
}
|
||||
|
||||
var statTabIndex = tabIndex
|
||||
val renderableFeatures = SpeciesFeatures
|
||||
.getFeaturesFor(pokemon.species)
|
||||
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
|
||||
.mapNotNull { it.getRenderer(pokemon) }
|
||||
|
||||
private fun drawTriangle(
|
||||
colour: Vector3f,
|
||||
@@ -178,6 +185,74 @@ class StatWidget(
|
||||
// drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
|
||||
}
|
||||
|
||||
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: MatrixStack, context: DrawContext, friendship: Int) {
|
||||
val barRatio = friendship / 255F
|
||||
val barWidth = ceil(barRatio * 108)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = statsOtherBarTemplate,
|
||||
x = moduleX,
|
||||
y = moduleY,
|
||||
height = 28,
|
||||
width = 124
|
||||
)
|
||||
|
||||
val red = 1
|
||||
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
|
||||
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = moduleX + 8,
|
||||
y = moduleY + 18,
|
||||
height = 8,
|
||||
width = barWidth,
|
||||
red = red,
|
||||
green = green,
|
||||
blue = blue
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = friendshipOverlayResource,
|
||||
x = moduleX / SCALE,
|
||||
y = (moduleY + 16) / SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.stats.friendship").bold(),
|
||||
x = moduleX + 62,
|
||||
y = moduleY + 2.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = friendship.toString().text(),
|
||||
x = moduleX + 11,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = "${floor(barRatio * 100)}%".text(),
|
||||
x = moduleX + 113,
|
||||
y = moduleY + 6,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
|
||||
val renderChart = statTabIndex != OTHER
|
||||
@@ -328,60 +403,28 @@ class StatWidget(
|
||||
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
|
||||
}
|
||||
} else {
|
||||
// Friendship
|
||||
val friendshipBarWidthMax = 108
|
||||
val friendshipRatio = pokemon.friendship / 255F
|
||||
val friendshipBarWidth = ceil(friendshipRatio * friendshipBarWidthMax)
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = CobblemonResources.WHITE,
|
||||
x = x + 13,
|
||||
y = y + 27,
|
||||
height = 8,
|
||||
width = friendshipBarWidth,
|
||||
red = 0.92,
|
||||
green = if (pokemon.friendship >= 160) 0.28 else 0.7,
|
||||
blue = if (pokemon.friendship >= 160) 0.4 else 0.28
|
||||
)
|
||||
var drawY = y + 11
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = friendshipOverlayResource,
|
||||
x = (x + 5) / SCALE,
|
||||
y = (y + 26) / SCALE,
|
||||
height = 20,
|
||||
width = 248,
|
||||
scale = SCALE
|
||||
)
|
||||
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
|
||||
drawY += 30
|
||||
|
||||
// Label
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = lang("ui.stats.friendship").bold(),
|
||||
x = x + 67,
|
||||
y = y + 12.5,
|
||||
centered = true,
|
||||
shadow = true
|
||||
)
|
||||
for (renderableFeature in renderableFeatures) {
|
||||
val rendered = renderableFeature.render(
|
||||
drawContext = context,
|
||||
x = x + 5F,
|
||||
y = drawY.toFloat(),
|
||||
pokemon = pokemon
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = pokemon.friendship.toString().text(),
|
||||
x = x + 16,
|
||||
y = y + 16,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
if (rendered) {
|
||||
drawY += 30
|
||||
}
|
||||
}
|
||||
|
||||
// for (value in summaries) {
|
||||
// drawBarModule(x + 5, drawY, matrices, context, value)
|
||||
// }
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = "${floor(friendshipRatio * 100)}%".text(),
|
||||
x = x + 118,
|
||||
y = y + 16,
|
||||
scale = SCALE,
|
||||
centered = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,14 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.keybind
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.DebugKeybindings
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.DownShiftPartyBinding
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.HidePartyBinding
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.PartySendBinding
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.UpShiftPartyBinding
|
||||
import com.cobblemon.mod.common.config.CobblemonConfig
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import net.minecraft.client.option.KeyBinding
|
||||
|
||||
@@ -23,13 +26,17 @@ import net.minecraft.client.option.KeyBinding
|
||||
* @since 2022-02-17
|
||||
*/
|
||||
object CobblemonKeyBinds {
|
||||
private val keyBinds = arrayListOf<CobblemonKeyBinding>()
|
||||
|
||||
init {
|
||||
PlatformEvents.CLIENT_TICK_POST.subscribe { this.onTick() }
|
||||
if (Cobblemon.config.enableDebugKeys) {
|
||||
DebugKeybindings.keybindings.forEach {
|
||||
this.queue(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val keyBinds = arrayListOf<CobblemonKeyBinding>()
|
||||
|
||||
val HIDE_PARTY = this.queue(HidePartyBinding)
|
||||
// ToDo enable again down the line
|
||||
//val POKENAV = this.queue(PokeNavigatorBinding)
|
||||
|
||||
+22
@@ -9,7 +9,9 @@
|
||||
package com.cobblemon.mod.common.client.keybind
|
||||
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.keybind.keybinds.SummaryBinding
|
||||
import com.cobblemon.mod.common.net.messages.server.starter.RequestStarterScreenPacket
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.MinecraftClient
|
||||
@@ -25,12 +27,20 @@ abstract class CobblemonPartyLockedKeyBinding(
|
||||
category: String
|
||||
) : CobblemonKeyBinding(name, type, key, category) {
|
||||
|
||||
private var skippedStarterSelectionMessageShown = false
|
||||
|
||||
override fun onTick() {
|
||||
if (this.wasPressed() && this.hasPartyMembers()) {
|
||||
this.onPress()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the player has any party members, and
|
||||
*
|
||||
* - Handles the starter selection if the player doesn't have any party members.
|
||||
* - Shows a message if the player initially skipped the starter selection.
|
||||
*/
|
||||
private fun hasPartyMembers(): Boolean {
|
||||
val havePokemon = CobblemonClient.storage.myParty.slots.any { it != null }
|
||||
val starterSelected = CobblemonClient.clientPlayerData.starterSelected
|
||||
@@ -42,6 +52,18 @@ abstract class CobblemonPartyLockedKeyBinding(
|
||||
RequestStarterScreenPacket().sendToServer()
|
||||
}
|
||||
return false
|
||||
} else if (!startersLocked && !starterSelected && havePokemon) {
|
||||
if (!skippedStarterSelectionMessageShown) {
|
||||
MinecraftClient.getInstance().player?.sendMessage(
|
||||
lang(
|
||||
"ui.starter.skippedchoosing",
|
||||
SummaryBinding.boundKey().localizedText
|
||||
).yellow(), false
|
||||
)
|
||||
/** Only show the info message about skipping the selection once per MC instance */
|
||||
skippedStarterSelectionMessageShown = true
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -10,4 +10,5 @@ package com.cobblemon.mod.common.client.keybind
|
||||
|
||||
object KeybindCategories {
|
||||
const val COBBLEMON_CATEGORY = "key.cobblemon.categories.cobblemon"
|
||||
const val COBBLEMON_DEBUG_CATEGORY = "key.cobblemon.categories.cobblemon.debug"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user