forked from mirrors/cobblemon
Compare commits
365
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfa830b008 | ||
|
|
f944b1e533 | ||
|
|
f67e581256 | ||
|
|
6a3cd46dd2 | ||
|
|
e63c7b9faf | ||
|
|
6b5a6e1fc5 | ||
|
|
0306259d39 | ||
|
|
aaea445900 | ||
|
|
9919358443 | ||
|
|
8b2619c79b | ||
|
|
6e2d62ddb8 | ||
|
|
c575ade2dc | ||
|
|
b3cd548774 | ||
|
|
556293860f | ||
|
|
50b921f4a4 | ||
|
|
4de5f0d5ac | ||
|
|
90f6ad327d | ||
|
|
312e9e75ed | ||
|
|
41e80aad63 | ||
|
|
81fd09f6d8 | ||
|
|
990127a3f4 | ||
|
|
e0b165b35a | ||
|
|
7fa01e05a0 | ||
|
|
73040f9a31 | ||
|
|
81258abaeb | ||
|
|
800d348611 | ||
|
|
d4dd48b3ff | ||
|
|
89d6bb2356 | ||
|
|
3a65425015 | ||
|
|
fad10da89f | ||
|
|
055ce8168c | ||
|
|
e4da05fd50 | ||
|
|
3a17a88ea2 | ||
|
|
09abf30421 | ||
|
|
06ea4b4cec | ||
|
|
8799308800 | ||
|
|
d727fde5b3 | ||
|
|
ce5e93b552 | ||
|
|
77c0138c09 | ||
|
|
0ded29dcad | ||
|
|
62fae979eb | ||
|
|
dc727fe56d | ||
|
|
4db36ef6fa | ||
|
|
e350d920b5 | ||
|
|
2a0ae1a466 | ||
|
|
3b8f7f23a3 | ||
|
|
72cf161f0d | ||
|
|
fe3d8d3185 | ||
|
|
d04ed7a519 | ||
|
|
e6e9137cb5 | ||
|
|
78f25b3f8b | ||
|
|
631293e0cd | ||
|
|
2865b8140a | ||
|
|
be34bc4c74 | ||
|
|
bae26162dd | ||
|
|
df8f078d13 | ||
|
|
309b223da9 | ||
|
|
75f688fa30 | ||
|
|
d7fadd58ed | ||
|
|
996c7189b6 | ||
|
|
e1d5f07aed | ||
|
|
6afe77ef0c | ||
|
|
bf54f28b58 | ||
|
|
aac10e967f | ||
|
|
0cd9da80d7 | ||
|
|
d33f1150d2 | ||
|
|
216ed85da1 | ||
|
|
4b70b20ab7 | ||
|
|
125e193b81 | ||
|
|
1ebd249351 | ||
|
|
74727cda48 | ||
|
|
6a8a2489d1 | ||
|
|
3524931596 | ||
|
|
43d9347c9c | ||
|
|
60303504cd | ||
|
|
63db0066d3 | ||
|
|
6e0270ac80 | ||
|
|
30070d35f6 | ||
|
|
8e3435479a | ||
|
|
333e1fe9ae | ||
|
|
c0c8c103f4 | ||
|
|
a414784cca | ||
|
|
b4b01ef0b6 | ||
|
|
04f725d72e | ||
|
|
0257f591de | ||
|
|
dfeeed8796 | ||
|
|
0e304a81e8 | ||
|
|
03acc21af1 | ||
|
|
2233e6903b | ||
|
|
ac281faa3b | ||
|
|
ad7a881fdc | ||
|
|
f9a2b5cc9c | ||
|
|
e2cc006c7c | ||
|
|
13240d28ea | ||
|
|
6891272516 | ||
|
|
eba784071a | ||
|
|
a4435af9c0 | ||
|
|
4b0b0f813e | ||
|
|
c32da0825c | ||
|
|
803703b6a6 | ||
|
|
668914275e | ||
|
|
af90ff6733 | ||
|
|
c657c2ea68 | ||
|
|
2f567830d4 | ||
|
|
569f3c624e | ||
|
|
f30be418f9 | ||
|
|
2c50dc2d0c | ||
|
|
dde2e9f52a | ||
|
|
50a21d91e8 | ||
|
|
58f3de13fa | ||
|
|
8d67a3a909 | ||
|
|
ad4cb8a290 | ||
|
|
d9e5de5f97 | ||
|
|
e4a47af06d | ||
|
|
cc741c0478 | ||
|
|
6887887d6b | ||
|
|
cf1100a20e | ||
|
|
ebc7b97e4f | ||
|
|
5324b7b19e | ||
|
|
5bddc31cbf | ||
|
|
d65ce9222a | ||
|
|
72bc2221af | ||
|
|
706221aead | ||
|
|
26bfca893b | ||
|
|
5f65f3c20c | ||
|
|
1e5239da10 | ||
|
|
7d8ec3470b | ||
|
|
389d2a0adc | ||
|
|
37433aa0c5 | ||
|
|
7fa0022095 | ||
|
|
9fde7a7618 | ||
|
|
6c1f7ddd70 | ||
|
|
1a1cedb3aa | ||
|
|
241b83cec9 | ||
|
|
f0feb81a16 | ||
|
|
b82287f258 | ||
|
|
9099cb4b5d | ||
|
|
99e0b7d995 | ||
|
|
00f4052b75 | ||
|
|
407c24b7c5 | ||
|
|
372f44538b | ||
|
|
c8904c3e38 | ||
|
|
6bfc711129 | ||
|
|
3d914f47ee | ||
|
|
61891d8fe4 | ||
|
|
0bec3ac851 | ||
|
|
1b66ee8ce3 | ||
|
|
c884c556cb | ||
|
|
5e6091c782 | ||
|
|
5a2e5479e0 | ||
|
|
9a8b481fa2 | ||
|
|
e2fb31fad3 | ||
|
|
fc03682140 | ||
|
|
07fbffaa71 | ||
|
|
17b8dd3532 | ||
|
|
5e2f5dadb4 | ||
|
|
4cf8a19d43 | ||
|
|
fc682d1cd1 | ||
|
|
7759c91f3b | ||
|
|
c13654f60c | ||
|
|
289e9df52b | ||
|
|
f205e7f29e | ||
|
|
cec031595f | ||
|
|
af007dad17 | ||
|
|
5dca5a1579 | ||
|
|
3ebad9dc9d | ||
|
|
c93fa02616 | ||
|
|
8490f3a923 | ||
|
|
9bf6200163 | ||
|
|
5897fff954 | ||
|
|
2fd2137d78 | ||
|
|
42c4d6625e | ||
|
|
b0381d1685 | ||
|
|
3b00043b13 | ||
|
|
2b403df6ef | ||
|
|
f0d5622bab | ||
|
|
ec28ca0c45 | ||
|
|
0d68eedc74 | ||
|
|
c969e1731b | ||
|
|
bb77c6d4a9 | ||
|
|
5dfc90c0bd | ||
|
|
26a72fe6a9 | ||
|
|
4bb9547822 | ||
|
|
ffe88d75b5 | ||
|
|
d8980fabbe | ||
|
|
7598413e93 | ||
|
|
b0cc4714fc | ||
|
|
5aa3f34880 | ||
|
|
21363405fc | ||
|
|
340be0b2a1 | ||
|
|
933e29b134 | ||
|
|
41b27e5d75 | ||
|
|
e80bf0e331 | ||
|
|
683aa354ed | ||
|
|
aa19ff2c95 | ||
|
|
7d5e790f75 | ||
|
|
76f17c9760 | ||
|
|
3b0d2cd980 | ||
|
|
7e43a7424e | ||
|
|
f5a3c93039 | ||
|
|
00dc501df7 | ||
|
|
39fee0bcea | ||
|
|
1162689172 | ||
|
|
b295644070 | ||
|
|
21df0085e2 | ||
|
|
b718daf0f1 | ||
|
|
08c4f4a0ff | ||
|
|
c4a1704a8b | ||
|
|
51af37b37a | ||
|
|
a3122812e4 | ||
|
|
837aa5acf5 | ||
|
|
19698fc0ff | ||
|
|
969d6ac7fc | ||
|
|
46bb9e1aec | ||
|
|
9792ff3392 | ||
|
|
4482a4f072 | ||
|
|
a92f37c740 | ||
|
|
4677dda922 | ||
|
|
81f7320e4a | ||
|
|
3c9cf42da4 | ||
|
|
620ed73fe9 | ||
|
|
978d9e76a1 | ||
|
|
7d09b7e2fe | ||
|
|
656cff2ab3 | ||
|
|
1d8462fd63 | ||
|
|
e417db6a8d | ||
|
|
16d0d3b7e1 | ||
|
|
dbedc58dd9 | ||
|
|
c0ea565967 | ||
|
|
7e92f5f314 | ||
|
|
df3ed93032 | ||
|
|
2335fd388e | ||
|
|
e152cc9fd0 | ||
|
|
3f7e2bef40 | ||
|
|
25927c6ebe | ||
|
|
2f18d6b05f | ||
|
|
2600e23233 | ||
|
|
5c5c72d800 | ||
|
|
2c812344f9 | ||
|
|
37b4577230 | ||
|
|
fcf64b00bf | ||
|
|
02b8cd81a0 | ||
|
|
def1b44f08 | ||
|
|
b28f915af9 | ||
|
|
29ce8b584a | ||
|
|
84f3a44109 | ||
|
|
08a58eee8a | ||
|
|
1e067cb7e4 | ||
|
|
c10f48090c | ||
|
|
2f464f82b1 | ||
|
|
9c0e5e487a | ||
|
|
95f2bbbf15 | ||
|
|
864d184cd9 | ||
|
|
a2256f6208 | ||
|
|
945d556b43 | ||
|
|
f0def2e883 | ||
|
|
79c644dad0 | ||
|
|
28c0e17a48 | ||
|
|
87e41bdb89 | ||
|
|
8a8505d816 | ||
|
|
1a19c51748 | ||
|
|
8a66ab9235 | ||
|
|
ee50361555 | ||
|
|
d3631f1436 | ||
|
|
31136435fe | ||
|
|
bdeb83c62c | ||
|
|
79e1f9143c | ||
|
|
15bf726421 | ||
|
|
7f1889db7b | ||
|
|
548c18bea4 | ||
|
|
d9203bef2a | ||
|
|
bef0b2fb87 | ||
|
|
d7bbc05af8 | ||
|
|
7ca3ab5e30 | ||
|
|
1bbb1201c8 | ||
|
|
8ed4fee8af | ||
|
|
a3dacc2c3d | ||
|
|
c265f73308 | ||
|
|
670dedc0e2 | ||
|
|
5e24799295 | ||
|
|
a7243102d5 | ||
|
|
de6a84a75b | ||
|
|
6e19ee6059 | ||
|
|
be3c35eb43 | ||
|
|
a865d59b99 | ||
|
|
8896440451 | ||
|
|
39b27a8c17 | ||
|
|
a0eb614f2a | ||
|
|
a6e4aeb321 | ||
|
|
bb796921b5 | ||
|
|
9f6f764aa1 | ||
|
|
f115a87b66 | ||
|
|
77c880ff2d | ||
|
|
16c64b9d8e | ||
|
|
caeb45e9e1 | ||
|
|
6e341438e9 | ||
|
|
7407483498 | ||
|
|
020c728004 | ||
|
|
7125987c98 | ||
|
|
6448c4472e | ||
|
|
eedac70d13 | ||
|
|
025e09cd12 | ||
|
|
4d2cfb63ea | ||
|
|
40794acdd5 | ||
|
|
82f8855725 | ||
|
|
5abcdbb837 | ||
|
|
2f817a7f9a | ||
|
|
67767fc53a | ||
|
|
43cd4b8403 | ||
|
|
2660d73bb6 | ||
|
|
faa7ca5d19 | ||
|
|
0fcbe51d97 | ||
|
|
b23ad62f99 | ||
|
|
3ca4ce8f80 | ||
|
|
446a994195 | ||
|
|
60e7ac828d | ||
|
|
a870effd3b | ||
|
|
236237a170 | ||
|
|
0fc67ba042 | ||
|
|
997ab29d91 | ||
|
|
e74f54f1d0 | ||
|
|
081e368551 | ||
|
|
1538f58a4a | ||
|
|
e5103e5d03 | ||
|
|
1cea15c9e5 | ||
|
|
3395fe0140 | ||
|
|
2eff8d14eb | ||
|
|
ed3cdf9b75 | ||
|
|
6e1ba0599f | ||
|
|
497d5ce39b | ||
|
|
660de3ea03 | ||
|
|
5ab3d7b493 | ||
|
|
2ce0069484 | ||
|
|
d87fa57c95 | ||
|
|
69947b0b32 | ||
|
|
f4c3f9ae04 | ||
|
|
04e1a6b41c | ||
|
|
6ae2adb2d0 | ||
|
|
0bf6932fe5 | ||
|
|
71382a3bd1 | ||
|
|
0f4f29518b | ||
|
|
8aeeece0a3 | ||
|
|
850b418c80 | ||
|
|
42af2898b2 | ||
|
|
b4508bf87e | ||
|
|
dcb4f52cdb | ||
|
|
1e9b390f38 | ||
|
|
7edd47f995 | ||
|
|
dd220c5c85 | ||
|
|
bea84a37d4 | ||
|
|
ac30da3c15 | ||
|
|
3861835d47 | ||
|
|
d62fb5c4c7 | ||
|
|
ca9dde92cd | ||
|
|
6ba264ddcb | ||
|
|
ab6c088ddf | ||
|
|
18105a8b3f | ||
|
|
acb5984a9e | ||
|
|
0a3ba8d4fa | ||
|
|
15259f0a95 | ||
|
|
9f66c053e1 | ||
|
|
743da2817b | ||
|
|
ebdf69f496 | ||
|
|
995364c960 | ||
|
|
289b2ddc7b |
@@ -3,3 +3,4 @@
|
||||
# are changed when only line endings change.
|
||||
src/generated/**/.cache/cache text eol=lf
|
||||
src/generated/**/*.json text eol=lf
|
||||
CHANGELOG.md merge=union
|
||||
+117
-19
@@ -1,43 +1,141 @@
|
||||
# Changelog
|
||||
|
||||
## [1.5.1](#1-5-1)
|
||||
## [1.5.3](#1-5-3)
|
||||
### Fixes
|
||||
- Scaled down Amaura's fetus model to avoid clipping through the tank while animating
|
||||
- Added Polished Tumblestone and Tumblestone Brick block sets from Tumblestone, Black Tumblestone, and Sky Tumblestone.
|
||||
- Fixed Cubone's cry not having a sound.
|
||||
- Fixed the Seafloor spawning context not being a usable context.
|
||||
- Fixed Pokemon spawning in non-full blocks like slabs.
|
||||
- Fixed Gilded Chests not dropping the chest itself when broken, only the contents.
|
||||
- Fixed Pokémon losing their Hidden Ability through evolution if the middle stage did not have a Hidden Ability.
|
||||
|
||||
### Additions
|
||||
- Added extra visuals to early berry growth stages
|
||||
|
||||
### Added cries to the following Pokémon
|
||||
- Timburr, Gurdurr, Conkeldurr
|
||||
- Golett, Golurk
|
||||
|
||||
### Changes
|
||||
- Altered the item model for Medicinal Leeks and Roasted Leeks to be held like sticks and other rod items.
|
||||
- Adjusted some berry balance values like yield and growth times
|
||||
- Changes to mulch buffs/durations
|
||||
|
||||
### Developer
|
||||
|
||||
### Data Pack & Resource Pack Creators
|
||||
- Added support for "shedders" similar to Shedinja's evolution logic.
|
||||
|
||||
## [1.5.2 (May 27th, 2024)](#1-5-2)
|
||||
### Fixes
|
||||
- Fixed Wooloo and Dubwool dyeing making your game start dying. Only if you do it on a server.
|
||||
- Fixed Gimmighoul causing PCs and parties to look glitched out, also only in a server.
|
||||
- Fixed players being able to dye other people's sheep Pokémon.
|
||||
|
||||
## [1.5.1 (May 27th, 2024)](#1-5-1)
|
||||
|
||||
### Additions
|
||||
- Added unique send out particles for Cherish, Dream, Beast, and Ancient Origin balls.
|
||||
- Made Wooloo and Dubwool dye-able like sheep
|
||||
- Made Wooloo and Dubwool dye-able like sheep. So cute!
|
||||
- Added stat up and down particles.
|
||||
- Most status effects now have particles! These include: Paralysis, Poison, Sleep, Confusion, Infatuation.
|
||||
#### Move Particle Effects
|
||||
- Confusion
|
||||
- Cotton Guard
|
||||
- Growl
|
||||
- Ice Punch
|
||||
- Fire Punch
|
||||
- Thunder Punch
|
||||
- Minimize
|
||||
- Quick Attack
|
||||
- Protect
|
||||
- Swords Dance
|
||||
- Sand Attack
|
||||
- Poison Powder
|
||||
- Sleep Powder
|
||||
- Stun Spore
|
||||
- Powder
|
||||
- Rage Powder
|
||||
- Spore
|
||||
- Thunder Wave
|
||||
|
||||
### Changes
|
||||
- Sounds for Relic Coin Sacks have been correctly renamed. Relic Coin Pouches received new sounds for breaking and placing.
|
||||
- Readjusted Petilil portraits
|
||||
- Changed "dimensions" spawn condition to check with dimension IDs instead of effects, so custom dimension IDs can be used.
|
||||
- Readjusted Petilil portraits so they fit a bit better.
|
||||
- Improved handling of Pokémon taken from the Restoration Tank block to be a bit more stable.
|
||||
- Made Mulch cheaper to craft.
|
||||
|
||||
### Fixes
|
||||
- Fixed a bug in which adding organic material to the fossil tank via right click was adding the full count of the stack currently in hand
|
||||
- Fixed a bug in which adding organic material to the restoration tank via right click was adding the full count of the stack currently in hand - but only taking 1 of the item.
|
||||
- Fixed a niche issue where some properties of entities were not initialized correctly, causing Pokémon that appeared to be level 1 until you battle them.
|
||||
- Fixed Fossilized Drake being missing from the Fossils item tag.
|
||||
- Fixed Gilded Chest block entity not being cleared on block break
|
||||
- Fixed sherd brokenness on Forge/Supplementaries incompat
|
||||
- Fixed Fossil Compartment crash with Jade / WAILA forks
|
||||
- Fixed PCStore#resize not allowing PC boxes size reduction.
|
||||
- Fixed Gilded Chest block entity not being cleared on block break, creating spooky ghost blocks. Old ones can be fixed by placing something like a furnace where it was, then breaking the furnace.
|
||||
- Fixed sherd brokenness on Forge.
|
||||
- Fixed Supplementaries incompatibility.
|
||||
- Fixed Fossil Compartment crash with Jade / WAILA forks.
|
||||
- Fixed pasture block PC lookups when the player is offline.
|
||||
- Fixed an untranslated battle message that occurs when using a move that just ran out of PP (e.g. Fire Blast that just got spited mid-turn).
|
||||
- Fixed held items being eaten even when the held item evolutions are already unlocked.
|
||||
- Fixed Hisuian Decidueye not being Grass/Fighting
|
||||
- Fixed both Decidueye forms learning both Triple Arrows and Spirit Shackle
|
||||
- Fixed Pineco being unable to evolve into Shulker Forretress
|
||||
- Fixed Kabutops T-posing when underwater. It still does not have proper swimming animations yet though.
|
||||
- Fixed Hisuian Decidueye not being Grass/Fighting.
|
||||
- Fixed both Decidueye forms learning both Triple Arrows and Spirit Shackle.
|
||||
- Fixed Pineco being unable to evolve into Shulker Forretress.
|
||||
- Fixed Kabutops T-posing when underwater. It doesn't have proper swimming animations yet, though.
|
||||
- Fixed Pidgey's missing walk animation.
|
||||
- Fixed recoil evo condition.
|
||||
- Fixed issue where poser debug tools didnt work on JSON posers
|
||||
- Fixed issue where gilded chests dont close when going far away
|
||||
|
||||
- Fixed Cyndaquil's hidden flames clipping if it was swimming.
|
||||
- Fixed Chimecho and Chingling being unable to spawn near bells. They are meant to!
|
||||
- Fixed Tyrantrum and Wailord Party Overlay models peeking through the chat box. It was kinda funny though.
|
||||
- Fixed hitbox sizes for Seedot, Nuzleaf, and Shiftry.
|
||||
- Fixed Budew and Lechonk sliding if they walked for too long.
|
||||
- Fixed Shedinja T-posing in battle.
|
||||
- Fixed recoil evolution condition not working, making things like Basculegion unobtainable.
|
||||
- Fixed issue where poser debug tools didn't work on JSON posers.
|
||||
- Fixed issue where gilded chests don't close when going far away.
|
||||
- Fixed issue where the restoration tank's renderer was reading old data, making it appear wrong.
|
||||
- Fixed issue where the lights on the restoration tank would not animate if it was facing east. Very specific.
|
||||
- Fixed client crash with the fossil machine when updating block state on a chunk that is unloaded in the client. I don't understand this but the devs are sure that all of those are real words.
|
||||
- Fixed Restoration Tank crash with Create upon the tank block's destruction.
|
||||
- Fixed Restoration Tank over consuming items when interacting with Create blocks.
|
||||
- Fixed addons that add very many moves to a learn-set causing disappearing Pokémon (visually) issues on servers.
|
||||
- Fixed Hyper Cutter and Big Pecks incorrectly stating that it prevented accuracy from being lowered in battle.
|
||||
- Fixed missing messages for Rough Skin and Iron Barbs in battle.
|
||||
- Fixed a bug where sometimes Pokémon sendouts wouldn't create an entity, or the entity would spawn at 0 0 0 which is not a good place for a Pokémon to be. Or any of us, really.
|
||||
- Fixed issue in which a locked gilded chest would animate to the open state when the client fails to open it, such as when it is locked.
|
||||
- Fixed a bug where aspects of a form would not be properly reflected on form changes (eg. Normal -> Hisui).
|
||||
- Fixed generic battle effect sounds not sounding the way they were intended to.
|
||||
- Fixed particle effects often not having access to some specific entity functions from MoLang.
|
||||
- Fixed particles sometimes lasting a single tick too long, causing (very quick) visual glitches.
|
||||
- Fixed particle rotations being inverted.
|
||||
- Fixed particle events not spawning at the instigating particle's location.
|
||||
- Fixed a bunch of spam during world generation.
|
||||
- Fixed a bug in which throwing a Poké Ball at a player owned Pokémon with the ability Illusion would reveal its true species. Hilarious meta strategy.
|
||||
- Fixed root-part animations not working for JSON posed Pokémon. You didn't notice this but if we didn't fix this in this update then if you use Quick Attack a lot you'd have seen a whole lot of [this](https://cdn.discordapp.com/attachments/1076993968803434627/1242660506783715369/Minecraft__1.20.1_-_Singleplayer_2024-05-21_22-08-17.mp4?ex=66549408&is=66534288&hm=ff95ee293eb15634fd63e6546534ea279540a1c892605e8d561593ca2c5600c5&) which is damn funny but very unintended.
|
||||
|
||||
### Developer
|
||||
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. More information this way.
|
||||
- Changed SpawnAction#complete to return a nullable generic R (the spawn action result) instead of a boolean. Provides more information this way.
|
||||
- Added an event that fires when a fossil is revived, with or without a player.
|
||||
- Added IVS and EVS property extractors.
|
||||
- Fixed PCStore#resize not allowing PC boxes size reduction.
|
||||
|
||||
### Data Pack & Resource Pack Creators
|
||||
- Added support for MoLang conditions for quirks and poses.
|
||||
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual.
|
||||
- Changed the AttackDefenceRatio requirement to StatCompare and StatEqual. There is some backwards compatibility for AttackDefenceRatio, though.
|
||||
- Changed "dimensions" spawn condition to check with dimension IDs instead of effects, so custom dimension IDs can be used.
|
||||
- Added parametric motion and rotation support to particle effects.
|
||||
- Added entity_scale as a molang var for particles (likely only applicable to Pokemon)
|
||||
- Added support for primary quirk animations using the following format:
|
||||
```JSON
|
||||
{
|
||||
"quirks": [
|
||||
"q.bedrock_primary_quirk('<pokemon>', '<animation>', <minSeconds>, <maxSeconds>, <loopTimes>, '<excludedLabels>', q.curve('<waveFunction>'))"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Simplified and Traditional Chinese.
|
||||
- Spanish.
|
||||
|
||||
## [1.5.0 - The Ruins and Revival Update (May 12th, 2024)](#1-5-0)
|
||||
#### "You're telling me that Mojang has added archaeology to the game? Hmm... that gives me an idea."
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin;
|
||||
|
||||
import com.cobblemon.mod.common.block.SaccharineLeafBlock;
|
||||
import net.minecraft.block.*;
|
||||
import net.minecraft.entity.EntityType;
|
||||
import net.minecraft.entity.Flutterer;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.mob.Angerable;
|
||||
import net.minecraft.entity.mob.PathAwareEntity;
|
||||
import net.minecraft.entity.passive.AnimalEntity;
|
||||
import net.minecraft.entity.passive.BeeEntity;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.sound.SoundEvents;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraft.world.WorldView;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.Unique;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Desc;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||
import org.spongepowered.asm.mixin.injection.Slice;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
|
||||
|
||||
|
||||
@Mixin(targets="net.minecraft.entity.passive.BeeEntity$GrowCropsGoal")
|
||||
public abstract class BeeEntityMixin {
|
||||
|
||||
@Unique
|
||||
private BlockState cobblemon$result = null;
|
||||
|
||||
@Inject(method = "tick()V", at = @At(
|
||||
value = "INVOKE",
|
||||
target = "Lnet/minecraft/block/Fertilizable;grow(Lnet/minecraft/server/world/ServerWorld;Lnet/minecraft/util/math/random/Random;Lnet/minecraft/util/math/BlockPos;Lnet/minecraft/block/BlockState;)V",
|
||||
shift = At.Shift.BY,
|
||||
by = 2
|
||||
),
|
||||
locals = LocalCapture.CAPTURE_FAILHARD
|
||||
)
|
||||
private void injectCustomGrowth(CallbackInfo ci, int i, BlockPos blockPos, BlockState blockState, Block block) {
|
||||
if (block instanceof SaccharineLeafBlock) {
|
||||
|
||||
int age = blockState.get(SaccharineLeafBlock.Companion.getAGE());
|
||||
|
||||
if (age < 2) {
|
||||
this.cobblemon$result = blockState.with(SaccharineLeafBlock.Companion.getAGE(), age + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ModifyVariable(method = "tick", at = @At(value = "LOAD", ordinal = 0), index = 5)
|
||||
private BlockState applyCustomBlockState(BlockState before) {
|
||||
if(this.cobblemon$result != null && before == null) {
|
||||
BlockState result = this.cobblemon$result;
|
||||
this.cobblemon$result = null;
|
||||
return result;
|
||||
}
|
||||
|
||||
return before;
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.cobblemon.mod.common.mixin.invoker;
|
||||
|
||||
import net.minecraft.resource.featuretoggle.FeatureSet;
|
||||
import net.minecraft.screen.ScreenHandler;
|
||||
import net.minecraft.screen.ScreenHandlerType;
|
||||
import net.minecraft.screen.ScreenHandlerType.Factory;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.gen.Invoker;
|
||||
|
||||
@Mixin(ScreenHandlerType.class)
|
||||
public interface ScreenHandlerTypeInvoker {
|
||||
@Invoker("<init>")
|
||||
static <T extends ScreenHandler> ScreenHandlerType<T> cobblemon$create(Factory<T> factory, FeatureSet requiredFeatures) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
@@ -371,36 +371,6 @@ object Cobblemon {
|
||||
BATTLE_VICTORY.subscribe { AdvancementHandler.onWinBattle(it) }
|
||||
EVOLUTION_COMPLETE.subscribe(Priority.LOWEST) { event ->
|
||||
AdvancementHandler.onEvolve(event)
|
||||
val pokemon = event.pokemon
|
||||
val ninjaskIdentifier = cobblemonResource("ninjask")
|
||||
// Ensure the config option is enabled and that the result was a ninjask and that shedinja exists
|
||||
if (this.config.ninjaskCreatesShedinja && pokemon.species.resourceIdentifier == ninjaskIdentifier && PokemonSpecies.getByIdentifier(Pokemon.SHEDINJA) != null) {
|
||||
val player = pokemon.getOwnerPlayer() ?: return@subscribe
|
||||
if (player.isCreative || player.inventory.containsAny { it.item is PokeBallItem }) {
|
||||
var pokeball = Items.AIR
|
||||
player.inventory.combinedInventory.forEach {
|
||||
it.forEach {
|
||||
itemStack -> if (itemStack.item is PokeBallItem && pokeball == Items.AIR) {
|
||||
pokeball = itemStack.item as PokeBallItem
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!player.isCreative) {
|
||||
player.inventory.removeAmountIf(1) { it.item is PokeBallItem }
|
||||
}
|
||||
if (pokeball == Items.AIR) {
|
||||
pokeball = CobblemonItems.POKE_BALL
|
||||
}
|
||||
val properties = event.evolution.result.copy()
|
||||
properties.species = Pokemon.SHEDINJA.toString()
|
||||
val product = pokemon.clone()
|
||||
product.removeHeldItem()
|
||||
properties.apply(product)
|
||||
product.caughtBall = (pokeball as PokeBallItem).pokeBall
|
||||
pokemon.storeCoordinates.get()?.store?.add(product)
|
||||
CobblemonCriteria.EVOLVE_POKEMON.trigger(player, EvolvePokemonContext(event.pokemon.preEvolution!!.species.resourceIdentifier, product.species.resourceIdentifier, playerData.get(player).advancementData.totalEvolvedCount))
|
||||
}
|
||||
}
|
||||
}
|
||||
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
|
||||
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
|
||||
|
||||
@@ -83,4 +83,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
|
||||
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
|
||||
BlockEntityType.Builder.create(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val COOKING_POT: BlockEntityType<CookingPotBlockEntity> = this.create("cooking_pot",
|
||||
BlockEntityType.Builder.create(::CookingPotBlockEntity, CobblemonBlocks.COOKING_POT).build(null)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonWallSignBlock
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.AbstractBlock.Settings
|
||||
import net.minecraft.block.enums.Instrument
|
||||
import net.minecraft.block.piston.PistonBehavior
|
||||
import net.minecraft.entity.effect.StatusEffects
|
||||
@@ -49,6 +50,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
|
||||
val APRICORN_BLOCK_SET_TYPE = BlockSetType("apricorn")
|
||||
val APRICORN_WOOD_TYPE = WoodType.register(WoodType("apricorn", APRICORN_BLOCK_SET_TYPE))
|
||||
val SACCHARINE_BLOCK_SET_TYPE = BlockSetType("saccharine")
|
||||
val SACCHARINE_WOOD_TYPE = WoodType.register(WoodType("saccharine", SACCHARINE_BLOCK_SET_TYPE))
|
||||
|
||||
// Evolution Ores
|
||||
@JvmField
|
||||
@@ -153,9 +156,52 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
@JvmField
|
||||
val YELLOW_APRICORN_SAPLING = this.create("yellow_apricorn_sapling", ApricornSaplingBlock(PLANT_PROPERTIES, Apricorn.YELLOW))
|
||||
|
||||
// Saccharines
|
||||
@JvmField
|
||||
val SACCHARINE_LOG = log("saccharine_log", arg2 = MapColor.BLUE)
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_LOG = this.create("stripped_saccharine_log", SaccharineStrippedLogBlock(Settings.create().mapColor(MapColor.LIGHT_BLUE).instrument(Instrument.BASS).strength(2.0F).sounds(BlockSoundGroup.WOOD).burnable())) //log("stripped_saccharine_log")
|
||||
@JvmField
|
||||
val SACCHARINE_WOOD = log("saccharine_wood")
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_WOOD = this.create("stripped_saccharine_wood", SaccharineStrippedLogBlock(Settings.create().mapColor(MapColor.LIGHT_BLUE).instrument(Instrument.BASS).strength(2.0F).sounds(BlockSoundGroup.WOOD).burnable())) //log("stripped_saccharine_log")
|
||||
//log("stripped_saccharine_wood")
|
||||
@JvmField
|
||||
val SACCHARINE_PLANKS = this.create("saccharine_planks", Block(AbstractBlock.Settings.create().mapColor(MapColor.LIGHT_BLUE).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = this.create("saccharine_leaves", SaccharineLeafBlock(Settings.create().mapColor(MapColor.LIGHT_BLUE).strength(0.2F).ticksRandomly().sounds(BlockSoundGroup.GRASS).nonOpaque().allowsSpawning { _, _, _, _ -> false }))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = this.create("saccharine_fence", FenceBlock(AbstractBlock.Settings.create().mapColor(SACCHARINE_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = this.create("saccharine_fence_gate", FenceGateBlock(AbstractBlock.Settings.create().mapColor(SACCHARINE_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField // Note At the time of 1.20.0 we don't need our own BlockSetType for SACCHARINE wood
|
||||
val SACCHARINE_BUTTON = this.create("saccharine_button", BlocksInvoker.createWoodenButtonBlock(BlockSetType.OAK))
|
||||
@JvmField
|
||||
val SACCHARINE_PRESSURE_PLATE = this.create("saccharine_pressure_plate", PressurePlateBlockInvoker.`cobblemon$create`(PressurePlateBlock.ActivationRule.EVERYTHING, AbstractBlock.Settings.create().mapColor(SACCHARINE_PLANKS.defaultMapColor).instrument(Instrument.BASS).noCollision().strength(0.5f).sounds(BlockSoundGroup.WOOD), SACCHARINE_BLOCK_SET_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", CobblemonSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_SIGN = this.create("saccharine_wall_sign", CobblemonWallSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_WALL_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", CobblemonHangingSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_WALL_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_WALL_HANGING_SIGN = this.create("saccharine_wall_hanging_sign", CobblemonWallHangingSignBlock(AbstractBlock.Settings.copy(Blocks.OAK_HANGING_SIGN), SACCHARINE_WOOD_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_SLAB = this.create("saccharine_slab", SlabBlock(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).instrument(Instrument.BASS).strength(2.0f, 3.0f).sounds(BlockSoundGroup.WOOD)))
|
||||
@JvmField
|
||||
val SACCHARINE_STAIRS = this.create("saccharine_stairs", StairsBlockInvoker.`cobblemon$create`(SACCHARINE_PLANKS.defaultState, AbstractBlock.Settings.copy(SACCHARINE_PLANKS)))
|
||||
@JvmField
|
||||
val SACCHARINE_DOOR = this.create("saccharine_door", DoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(SACCHARINE_PLANKS.defaultMapColor).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque(), SACCHARINE_BLOCK_SET_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_TRAPDOOR = this.create("saccharine_trapdoor", TrapdoorBlockInvoker.`cobblemon$create`(AbstractBlock.Settings.create().mapColor(MapColor.OAK_TAN).instrument(Instrument.BASS).strength(3.0F).sounds(BlockSoundGroup.WOOD).nonOpaque().allowsSpawning { _, _, _, _ -> false }, SACCHARINE_BLOCK_SET_TYPE))
|
||||
@JvmField
|
||||
val SACCHARINE_SAPLING = this.create("saccharine_sapling", SaccharineSaplingBlock(PLANT_PROPERTIES))
|
||||
|
||||
@JvmField
|
||||
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 BUGWORT = this.create("bugwort", BugwortBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.PALE_PURPLE).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(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(CobblemonSounds.BIG_ROOT_SOUNDS)))
|
||||
@@ -214,6 +260,213 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE_STAIRS = this.create("polished_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_TUMBLESTONE)))
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE_SLAB = this.create("polished_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE_WALL = this.create("polished_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val CHISELED_POLISHED_TUMBLESTONE = this.create("chiseled_polished_tumblestone", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICKS = this.create("tumblestone_bricks", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICK_STAIRS = this.create("tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(TUMBLESTONE_BRICKS)))
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICK_SLAB = this.create("tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICK_WALL = this.create("tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val CHISELED_TUMBLESTONE_BRICKS = this.create("chiseled_tumblestone_bricks", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_ORANGE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE = this.create("polished_sky_tumblestone", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE_STAIRS = this.create("polished_sky_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_SKY_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_SKY_TUMBLESTONE)))
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE_SLAB = this.create("polished_sky_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE_WALL = this.create("polished_sky_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val CHISELED_POLISHED_SKY_TUMBLESTONE = this.create("chiseled_polished_sky_tumblestone", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICKS = this.create("sky_tumblestone_bricks", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICK_STAIRS = this.create("sky_tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(
|
||||
SKY_TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(SKY_TUMBLESTONE_BRICKS)))
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICK_SLAB = this.create("sky_tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICK_WALL = this.create("sky_tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val CHISELED_SKY_TUMBLESTONE_BRICKS = this.create("chiseled_sky_tumblestone_bricks", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.LIGHT_BLUE)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE = this.create("polished_black_tumblestone", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE_STAIRS = this.create("polished_black_tumblestone_stairs", StairsBlockInvoker.`cobblemon$create`(POLISHED_BLACK_TUMBLESTONE.defaultState, AbstractBlock.Settings.copy(POLISHED_BLACK_TUMBLESTONE)))
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE_SLAB = this.create("polished_black_tumblestone_slab", SlabBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE_WALL = this.create("polished_black_tumblestone_wall", WallBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val CHISELED_POLISHED_BLACK_TUMBLESTONE = this.create("chiseled_polished_black_tumblestone", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICKS = this.create("black_tumblestone_bricks", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICK_STAIRS = this.create("black_tumblestone_brick_stairs", StairsBlockInvoker.`cobblemon$create`(
|
||||
BLACK_TUMBLESTONE_BRICKS.defaultState, AbstractBlock.Settings.copy(BLACK_TUMBLESTONE_BRICKS)))
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICK_SLAB = this.create("black_tumblestone_brick_slab", SlabBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICK_WALL = this.create("black_tumblestone_brick_wall", WallBlock(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
@JvmField
|
||||
val CHISELED_BLACK_TUMBLESTONE_BRICKS = this.create("chiseled_black_tumblestone_bricks", Block(AbstractBlock.Settings.create()
|
||||
.mapColor(MapColor.TERRACOTTA_BLACK)
|
||||
.strength(1.0F)
|
||||
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
|
||||
.requiresTool()
|
||||
.instrument(Instrument.BASEDRUM)
|
||||
))
|
||||
|
||||
@JvmField
|
||||
val BLACK_APRICORN = apricornBlock("black_apricorn", Apricorn.BLACK)
|
||||
@JvmField
|
||||
@@ -401,6 +654,32 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val COOKING_POT = create(
|
||||
"cooking_pot",
|
||||
CookingPotBlock(
|
||||
Settings.create()
|
||||
.sounds(CobblemonSounds.DISPLAY_CASE_SOUNDS)
|
||||
.nonOpaque()
|
||||
.pistonBehavior(PistonBehavior.BLOCK)
|
||||
.mapColor(MapColor.STONE_GRAY)
|
||||
.strength(0.3f)
|
||||
)
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val SWEET_INCENSE = create(
|
||||
"sweet_incense",
|
||||
SweetIncenseBlock(
|
||||
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(CobblemonSounds.MINT_SOUNDS)))
|
||||
@@ -441,7 +720,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
*/
|
||||
fun strippedBlocks(): Map<Block, Block> = mapOf(
|
||||
APRICORN_WOOD to STRIPPED_APRICORN_WOOD,
|
||||
APRICORN_LOG to STRIPPED_APRICORN_LOG
|
||||
APRICORN_LOG to STRIPPED_APRICORN_LOG,
|
||||
SACCHARINE_WOOD to STRIPPED_SACCHARINE_WOOD,
|
||||
SACCHARINE_LOG to STRIPPED_SACCHARINE_LOG
|
||||
)
|
||||
|
||||
|
||||
@@ -528,6 +809,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val WEPEAR_BERRY = this.berryBlock("wepear")
|
||||
val WIKI_BERRY = this.berryBlock("wiki")
|
||||
val YACHE_BERRY = this.berryBlock("yache")
|
||||
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(AbstractBlock.Settings.create()))
|
||||
|
||||
init {
|
||||
/**
|
||||
@@ -592,4 +874,14 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
|
||||
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
|
||||
return this.create(name, block)
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method for creating saccharine leaves
|
||||
* similar to Vanilla
|
||||
private fun saccharineLeaves(name: String): SaccharineLeafBlock {
|
||||
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
|
||||
|
||||
|
||||
return this.create(name, block)
|
||||
}*/
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import net.minecraft.registry.tag.TagKey
|
||||
import net.minecraft.resource.ResourceManager
|
||||
import net.minecraft.resource.ResourceReloader
|
||||
import net.minecraft.resource.ResourceType
|
||||
import net.minecraft.screen.ScreenHandlerType
|
||||
import net.minecraft.server.MinecraftServer
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.Text
|
||||
@@ -158,6 +159,8 @@ interface CobblemonImplementation {
|
||||
*/
|
||||
fun registerResourceReloader(identifier: Identifier, reloader: ResourceReloader, type: ResourceType, dependencies: Collection<Identifier>)
|
||||
|
||||
fun registerScreenHandlerType(identifier: Identifier, screenHandlerType: ScreenHandlerType<*>)
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.BugwortBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock
|
||||
import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
|
||||
@@ -23,6 +24,7 @@ import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
|
||||
import com.cobblemon.mod.common.item.battle.DireHitItem
|
||||
import com.cobblemon.mod.common.item.battle.GuardSpecItem
|
||||
import com.cobblemon.mod.common.item.battle.XStatItem
|
||||
import com.cobblemon.mod.common.item.berry.BerryItem
|
||||
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.HealingBerryItem
|
||||
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
|
||||
@@ -242,6 +244,57 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val GIMMIGHOUL_CHEST = this.create("gimmighoul_chest", BlockItem(CobblemonBlocks.GIMMIGHOUL_CHEST, Item.Settings()))
|
||||
|
||||
// Saccharines
|
||||
@JvmField
|
||||
val SACCHARINE_LOG = blockItem("saccharine_log", CobblemonBlocks.SACCHARINE_LOG)
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_LOG = blockItem("stripped_saccharine_log", CobblemonBlocks.STRIPPED_SACCHARINE_LOG)
|
||||
@JvmField
|
||||
val SACCHARINE_WOOD = blockItem("saccharine_wood", CobblemonBlocks.SACCHARINE_WOOD)
|
||||
@JvmField
|
||||
val STRIPPED_SACCHARINE_WOOD = blockItem("stripped_saccharine_wood", CobblemonBlocks.STRIPPED_SACCHARINE_WOOD)
|
||||
@JvmField
|
||||
val SACCHARINE_PLANKS = blockItem("saccharine_planks", CobblemonBlocks.SACCHARINE_PLANKS)
|
||||
@JvmField
|
||||
val SACCHARINE_LEAVES = compostableBlockItem("saccharine_leaves", CobblemonBlocks.SACCHARINE_LEAVES)
|
||||
@JvmField
|
||||
val SACCHARINE_BOAT = create("saccharine_boat", CobblemonBoatItem(CobblemonBoatType.SACCHARINE, false, Item.Settings().maxCount(1)))
|
||||
@JvmField
|
||||
val SACCHARINE_CHEST_BOAT = create("saccharine_chest_boat", CobblemonBoatItem(CobblemonBoatType.SACCHARINE, true, Item.Settings().maxCount(1)))
|
||||
|
||||
@JvmField
|
||||
val SACCHARINE_DOOR = blockItem("saccharine_door", CobblemonBlocks.SACCHARINE_DOOR)
|
||||
@JvmField
|
||||
val SACCHARINE_TRAPDOOR = blockItem("saccharine_trapdoor", CobblemonBlocks.SACCHARINE_TRAPDOOR)
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE = blockItem("saccharine_fence", CobblemonBlocks.SACCHARINE_FENCE)
|
||||
@JvmField
|
||||
val SACCHARINE_FENCE_GATE = blockItem("saccharine_fence_gate", CobblemonBlocks.SACCHARINE_FENCE_GATE)
|
||||
@JvmField
|
||||
val SACCHARINE_BUTTON = blockItem("saccharine_button", CobblemonBlocks.SACCHARINE_BUTTON)
|
||||
@JvmField
|
||||
val SACCHARINE_PRESSURE_PLATE = blockItem("saccharine_pressure_plate", CobblemonBlocks.SACCHARINE_PRESSURE_PLATE)
|
||||
@JvmField
|
||||
val SACCHARINE_SLAB = blockItem("saccharine_slab", CobblemonBlocks.SACCHARINE_SLAB)
|
||||
@JvmField
|
||||
val SACCHARINE_STAIRS = blockItem("saccharine_stairs", CobblemonBlocks.SACCHARINE_STAIRS)
|
||||
@JvmField
|
||||
val SACCHARINE_SIGN = this.create("saccharine_sign", SignItem(Item.Settings().maxCount(16), CobblemonBlocks.SACCHARINE_SIGN, CobblemonBlocks.SACCHARINE_WALL_SIGN))
|
||||
@JvmField
|
||||
val SACCHARINE_HANGING_SIGN = this.create("saccharine_hanging_sign", HangingSignItem(CobblemonBlocks.SACCHARINE_HANGING_SIGN, CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
|
||||
@JvmField
|
||||
val SACCHARINE_SAPLING = saccharineSaplingItem("saccharine_sapling", SaccharineSaplingItem(CobblemonBlocks.SACCHARINE_SAPLING))
|
||||
|
||||
@JvmField
|
||||
val SWEET_SAP = noSettingsItem("sweet_sap")
|
||||
@JvmField
|
||||
val BUGWORT = bugwortItem("bugwort", BugwortItem(CobblemonBlocks.BUGWORT))
|
||||
|
||||
@JvmField
|
||||
val SCATTER_BANG = this.create("scatter_bang", ScatterBangItem(Item.Settings()))
|
||||
@JvmField
|
||||
val STICKY_GLOB = this.create("sticky_glob", StickyGlobItem(Item.Settings()))
|
||||
|
||||
@JvmField
|
||||
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
|
||||
@JvmField
|
||||
@@ -256,6 +309,10 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
|
||||
@JvmField
|
||||
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
|
||||
@JvmField
|
||||
val COOKING_POT = blockItem("cooking_pot", CobblemonBlocks.COOKING_POT)
|
||||
@JvmField
|
||||
val SWEET_INCENSE = blockItem("sweet_incense", CobblemonBlocks.SWEET_INCENSE)
|
||||
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@@ -932,6 +989,67 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BLOCK = blockItem("black_tumblestone_block", CobblemonBlocks.BLACK_TUMBLESTONE_BLOCK)
|
||||
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE = blockItem("polished_tumblestone", CobblemonBlocks.POLISHED_TUMBLESTONE)
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE_STAIRS = blockItem("polished_tumblestone_stairs", CobblemonBlocks.POLISHED_TUMBLESTONE_STAIRS)
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE_SLAB = blockItem("polished_tumblestone_slab", CobblemonBlocks.POLISHED_TUMBLESTONE_SLAB)
|
||||
@JvmField
|
||||
val POLISHED_TUMBLESTONE_WALL = blockItem("polished_tumblestone_wall", CobblemonBlocks.POLISHED_TUMBLESTONE_WALL)
|
||||
@JvmField
|
||||
val CHISELED_POLISHED_TUMBLESTONE = blockItem("chiseled_polished_tumblestone", CobblemonBlocks.CHISELED_POLISHED_TUMBLESTONE)
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICKS = blockItem("tumblestone_bricks", CobblemonBlocks.TUMBLESTONE_BRICKS)
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICK_STAIRS = blockItem("tumblestone_brick_stairs", CobblemonBlocks.TUMBLESTONE_BRICK_STAIRS)
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICK_SLAB = blockItem("tumblestone_brick_slab", CobblemonBlocks.TUMBLESTONE_BRICK_SLAB)
|
||||
@JvmField
|
||||
val TUMBLESTONE_BRICK_WALL = blockItem("tumblestone_brick_wall", CobblemonBlocks.TUMBLESTONE_BRICK_WALL)
|
||||
@JvmField
|
||||
val CHISELED_TUMBLESTONE_BRICKS = blockItem("chiseled_tumblestone_bricks", CobblemonBlocks.CHISELED_TUMBLESTONE_BRICKS)
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE = blockItem("polished_sky_tumblestone", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE_STAIRS = blockItem("polished_sky_tumblestone_stairs", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_STAIRS)
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE_SLAB = blockItem("polished_sky_tumblestone_slab", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_SLAB)
|
||||
@JvmField
|
||||
val POLISHED_SKY_TUMBLESTONE_WALL = blockItem("polished_sky_tumblestone_wall", CobblemonBlocks.POLISHED_SKY_TUMBLESTONE_WALL)
|
||||
@JvmField
|
||||
val CHISELED_POLISHED_SKY_TUMBLESTONE = blockItem("chiseled_polished_sky_tumblestone", CobblemonBlocks.CHISELED_POLISHED_SKY_TUMBLESTONE)
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICKS = blockItem("sky_tumblestone_bricks", CobblemonBlocks.SKY_TUMBLESTONE_BRICKS)
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICK_STAIRS = blockItem("sky_tumblestone_brick_stairs", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_STAIRS)
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICK_SLAB = blockItem("sky_tumblestone_brick_slab", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_SLAB)
|
||||
@JvmField
|
||||
val SKY_TUMBLESTONE_BRICK_WALL = blockItem("sky_tumblestone_brick_wall", CobblemonBlocks.SKY_TUMBLESTONE_BRICK_WALL)
|
||||
@JvmField
|
||||
val CHISELED_SKY_TUMBLESTONE_BRICKS = blockItem("chiseled_sky_tumblestone_bricks", CobblemonBlocks.CHISELED_SKY_TUMBLESTONE_BRICKS)
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE = blockItem("polished_black_tumblestone", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE_STAIRS = blockItem("polished_black_tumblestone_stairs", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_STAIRS)
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE_SLAB = blockItem("polished_black_tumblestone_slab", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_SLAB)
|
||||
@JvmField
|
||||
val POLISHED_BLACK_TUMBLESTONE_WALL = blockItem("polished_black_tumblestone_wall", CobblemonBlocks.POLISHED_BLACK_TUMBLESTONE_WALL)
|
||||
@JvmField
|
||||
val CHISELED_POLISHED_BLACK_TUMBLESTONE = blockItem("chiseled_polished_black_tumblestone", CobblemonBlocks.CHISELED_POLISHED_BLACK_TUMBLESTONE)
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICKS = blockItem("black_tumblestone_bricks", CobblemonBlocks.BLACK_TUMBLESTONE_BRICKS)
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICK_STAIRS = blockItem("black_tumblestone_brick_stairs", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_STAIRS)
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICK_SLAB = blockItem("black_tumblestone_brick_slab", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_SLAB)
|
||||
@JvmField
|
||||
val BLACK_TUMBLESTONE_BRICK_WALL = blockItem("black_tumblestone_brick_wall", CobblemonBlocks.BLACK_TUMBLESTONE_BRICK_WALL)
|
||||
@JvmField
|
||||
val CHISELED_BLACK_TUMBLESTONE_BRICKS = blockItem("chiseled_black_tumblestone_bricks", CobblemonBlocks.CHISELED_BLACK_TUMBLESTONE_BRICKS)
|
||||
|
||||
@JvmField
|
||||
val AUTOMATON_ARMOR_TRIM_SMITHING_TEMPLATE: SmithingTemplateItem = this.create(
|
||||
"automaton_armor_trim_smithing_template",
|
||||
@@ -985,6 +1103,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
val STEEL_GEM = noSettingsItem("steel_gem")
|
||||
@JvmField
|
||||
val FAIRY_GEM = noSettingsItem("fairy_gem")
|
||||
//@JvmField
|
||||
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
|
||||
|
||||
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
|
||||
|
||||
@@ -1061,6 +1181,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
|
||||
return item
|
||||
}
|
||||
|
||||
private fun saccharineSaplingItem(name: String, saccharineSaplingItem: SaccharineSaplingItem): SaccharineSaplingItem {
|
||||
val finalName = "saccharine_sapling"
|
||||
val item = this.create(finalName, saccharineSaplingItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun bugwortItem(name: String, bugwortItem: BugwortItem): BugwortItem {
|
||||
val finalName = "bugwort"
|
||||
val item = this.create(finalName, bugwortItem)
|
||||
compostable(item, .65f)
|
||||
return item
|
||||
}
|
||||
|
||||
private fun mintSeed(name: String, mintBlock: MintBlock): Item {
|
||||
val finalName = "${name}_mint_seeds"
|
||||
val item = this.blockItem(finalName, mintBlock)
|
||||
|
||||
@@ -235,6 +235,7 @@ object CobblemonNetwork : NetworkManager {
|
||||
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
this.createClientBound(FormUpdatePacket.ID, FormUpdatePacket::decode, PokemonUpdatePacketHandler())
|
||||
|
||||
// Evolution start
|
||||
this.createClientBound(AddEvolutionPacket.ID, AddEvolutionPacket::decode, PokemonUpdatePacketHandler())
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.item.BerryItem
|
||||
import com.cobblemon.mod.common.item.berry.BerryItem
|
||||
import com.cobblemon.mod.common.pokemon.Nature
|
||||
import com.cobblemon.mod.common.util.readBox
|
||||
import com.cobblemon.mod.common.util.writeBox
|
||||
@@ -45,9 +45,8 @@ import net.minecraft.world.biome.Biome
|
||||
* @property identifier The [Identifier] of this berry.
|
||||
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
|
||||
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
|
||||
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
|
||||
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
|
||||
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
|
||||
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow frome age 0 to age 3
|
||||
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to grow from age 3 to age 5
|
||||
* @property favoriteMulches The types of mulches that benefit this berries yield
|
||||
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
|
||||
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
|
||||
@@ -60,6 +59,7 @@ import net.minecraft.world.biome.Biome
|
||||
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
|
||||
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
|
||||
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
|
||||
* @property stageOnePositioning Transformation of the berry model for age 0
|
||||
*
|
||||
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
|
||||
*/
|
||||
@@ -69,7 +69,6 @@ class Berry(
|
||||
val preferredBiomeTags: List<TagKey<Biome>>,
|
||||
val growthTime: IntRange,
|
||||
val refreshRate: IntRange,
|
||||
//val lifeCycles: IntRange,
|
||||
val favoriteMulches: EnumSet<MulchVariant>,
|
||||
val growthFactors: Collection<GrowthFactor>,
|
||||
val spawnConditions: List<BerrySpawnCondition>,
|
||||
@@ -88,7 +87,9 @@ class Berry(
|
||||
@SerializedName("fruitModel")
|
||||
val fruitModelIdentifier: Identifier,
|
||||
val fruitTexture: Identifier,
|
||||
val weight: Float
|
||||
val stageOnePositioning: GrowthPoint,
|
||||
val weight: Float,
|
||||
val boneMealChance: Float
|
||||
) {
|
||||
|
||||
@Transient
|
||||
@@ -161,8 +162,9 @@ class Berry(
|
||||
val bonus = this.bonusYield(world, state, pos)
|
||||
var yield = base + bonus.first
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
|
||||
yield = min(yield + 1, maxYield())
|
||||
if (BerryBlock.getMulch(treeEntity) == MulchVariant.RICH) {
|
||||
//I hope the upper bound isnt exclusive, especially when there's a method called nextBetweenExclusive
|
||||
yield += world.random.nextBetween(1, 3)
|
||||
treeEntity.decrementMulchDuration(world, pos, state)
|
||||
}
|
||||
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
|
||||
@@ -279,6 +281,13 @@ class Berry(
|
||||
buffer.writeIdentifier(this.flowerTexture)
|
||||
buffer.writeIdentifier(this.fruitModelIdentifier)
|
||||
buffer.writeIdentifier(this.fruitTexture)
|
||||
buffer.writeDouble(stageOnePositioning.position.x)
|
||||
buffer.writeDouble(stageOnePositioning.position.y)
|
||||
buffer.writeDouble(stageOnePositioning.position.z)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.x)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.y)
|
||||
buffer.writeDouble(stageOnePositioning.rotation.z)
|
||||
buffer.writeFloat(boneMealChance)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -292,7 +301,8 @@ class Berry(
|
||||
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
|
||||
var bonus = 0
|
||||
val passed = arrayListOf<GrowthFactor>()
|
||||
val mulchVariant = BerryBlock.getMulch(state)
|
||||
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return 0 to passed
|
||||
val mulchVariant = BerryBlock.getMulch(treeEntity)
|
||||
val hasBiomeMulch = mulchVariant in favoriteMulches
|
||||
this.growthFactors.forEach { factor ->
|
||||
if (factor.isValid(world, state, pos)) {
|
||||
@@ -339,7 +349,9 @@ class Berry(
|
||||
val flowerTexture = buffer.readIdentifier()
|
||||
val fruitModelIdentifier = buffer.readIdentifier()
|
||||
val fruitTexture = buffer.readIdentifier()
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
|
||||
val stageOneYPos = GrowthPoint(Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()), Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble()))
|
||||
val boneMealChance = buffer.readFloat()
|
||||
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, stageOneYPos, 0F, boneMealChance)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -78,6 +78,8 @@ object CobblemonEvents {
|
||||
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
|
||||
@JvmField
|
||||
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
|
||||
@JvmField
|
||||
val FOSSIL_REVIVED = EventObservable<FossilRevivedEvent>()
|
||||
// @JvmField
|
||||
// val EGG_HATCH = EventObservable<HatchEggEvent>()
|
||||
@JvmField
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.events.pokemon
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
|
||||
data class FossilRevivedEvent (
|
||||
val pokemon: Pokemon,
|
||||
val player: ServerPlayerEntity?
|
||||
)
|
||||
+4
-2
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.moves.animations
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.cobblemon.mod.common.api.moves.animations.keyframes.ActionEffectKeyframe
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.resolveBoolean
|
||||
import java.util.concurrent.CompletableFuture
|
||||
@@ -43,6 +41,9 @@ class ActionEffectTimeline(
|
||||
// .toList copy because I'm paranoid about iterators being trying to share between identical effects playing
|
||||
chainKeyframes(context, timeline.toList().iterator(), finalFuture)
|
||||
finalFuture
|
||||
}.exceptionallyCompose {
|
||||
it.printStackTrace()
|
||||
CompletableFuture.completedFuture(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,6 +56,7 @@ class ActionEffectTimeline(
|
||||
keyframe.play(context)
|
||||
.thenRun { context.currentKeyframes.remove(keyframe) }
|
||||
.thenApply { chainKeyframes(context, iterator, finalFuture) }
|
||||
.exceptionally { finalFuture.completeExceptionally(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.moves.animations.keyframes
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.EntityProvider
|
||||
@@ -40,7 +39,7 @@ class EntityParticlesActionEffectKeyframe : ConditionalActionEffectKeyframe(), E
|
||||
.flatMap { prov -> prov.entities.filter { test(context, it, isUser = prov is UsersProvider) } }
|
||||
|
||||
val effectIdentifier = try {
|
||||
effect?.asExpressionLike()?.resolveString(context.runtime)
|
||||
effect?.asExpressionLike()?.resolveString(context.runtime)?.takeIf { it != "0.0" } ?: effect
|
||||
} catch (e: Exception) {
|
||||
effect
|
||||
}?.asIdentifierDefaultingNamespace() ?: return skip()
|
||||
|
||||
@@ -8,24 +8,30 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.mulch
|
||||
|
||||
import com.cobblemon.mod.common.BakingOverride
|
||||
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
|
||||
import net.minecraft.util.StringIdentifiable
|
||||
|
||||
/**
|
||||
* Represents the different types of Mulch implemented in the mod.
|
||||
*
|
||||
*/
|
||||
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
|
||||
COARSE,
|
||||
GROWTH(3),
|
||||
HUMID,
|
||||
LOAMY,
|
||||
PEAT,
|
||||
RICH(3),
|
||||
SANDY,
|
||||
SURPRISE(3),
|
||||
NONE;
|
||||
enum class MulchVariant(val model: BakingOverride?, val duration: Int = -1) : StringIdentifiable {
|
||||
COARSE(CobblemonBakingOverrides.COARSE_MULCH),
|
||||
GROWTH(CobblemonBakingOverrides.GROWTH_MULCH, 5),
|
||||
HUMID(CobblemonBakingOverrides.HUMID_MULCH),
|
||||
LOAMY(CobblemonBakingOverrides.LOAMY_MULCH),
|
||||
PEAT(CobblemonBakingOverrides.PEAT_MULCH),
|
||||
RICH(CobblemonBakingOverrides.RICH_MULCH, 5),
|
||||
SANDY(CobblemonBakingOverrides.SANDY_MULCH),
|
||||
SURPRISE(CobblemonBakingOverrides.SURPRISE_MULCH, 3),
|
||||
NONE(null);
|
||||
|
||||
override fun asString(): String {
|
||||
return name.lowercase()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val GROWTH_TIME_MULTIPLIER = 0.5
|
||||
}
|
||||
}
|
||||
|
||||
@@ -52,11 +52,9 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
|
||||
if(hand == Hand.OFF_HAND) {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
if (!world.isClient) {
|
||||
val entity = world.getBlockEntity(pos) as MultiblockEntity?
|
||||
if (entity?.multiblockStructure != null) {
|
||||
return entity.multiblockStructure!!.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
+4
@@ -47,6 +47,10 @@ fun interface PokemonPropertyExtractor {
|
||||
val NICKNAME = add { pokemon, properties -> properties.nickname = pokemon.nickname }
|
||||
@JvmField
|
||||
val STATUS = add { pokemon, properties -> properties.status = pokemon.status?.status?.showdownName }
|
||||
@JvmField
|
||||
val IVS = add { pokemon, properties -> properties.ivs = pokemon.ivs }
|
||||
@JvmField
|
||||
val EVS = add { pokemon, properties -> properties.evs = pokemon.evs }
|
||||
|
||||
@JvmField
|
||||
val ILLUSION = mutableListOf(SPECIES, FORM, ASPECTS, GENDER, NICKNAME, SHINY)
|
||||
|
||||
@@ -8,7 +8,11 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.evolution
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.playerData
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.advancement.criterion.EvolvePokemonContext
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEvent
|
||||
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
|
||||
@@ -17,7 +21,9 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.requirement.EvolutionRequirement
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
@@ -25,6 +31,7 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.sound.SoundCategory
|
||||
|
||||
@@ -44,6 +51,11 @@ interface Evolution : EvolutionLike {
|
||||
*/
|
||||
val result: PokemonProperties
|
||||
|
||||
/**
|
||||
* The shed result of this evolution.
|
||||
*/
|
||||
val shedder: PokemonProperties?
|
||||
|
||||
/**
|
||||
* If this evolution allows the user to choose when to start it or not.
|
||||
*/
|
||||
@@ -95,6 +107,36 @@ interface Evolution : EvolutionLike {
|
||||
return true
|
||||
}
|
||||
|
||||
fun shed(pokemon: Pokemon): Boolean {
|
||||
val innerShedder = shedder ?: return false
|
||||
|
||||
val owner = pokemon.getOwnerPlayer() ?: return false
|
||||
// If the player has at least one Pokeball in their inventory.
|
||||
var pokeballStack: ItemStack? = null
|
||||
if (!owner.isCreative) {
|
||||
for (i in 0 until owner.inventory.size()) {
|
||||
val stackI = owner.inventory.getStack(i)
|
||||
if (stackI.isIn(CobblemonItemTags.POKE_BALLS)) {
|
||||
pokeballStack = stackI
|
||||
}
|
||||
}
|
||||
if (pokeballStack == null) return false
|
||||
}
|
||||
// If the player has at least one empty spot in their party.
|
||||
val emptySlot = owner.party().getFirstAvailablePosition() ?: return false
|
||||
|
||||
// Add shed Pokemon to player's party
|
||||
val shedPokemon = pokemon.clone()
|
||||
shedPokemon.removeHeldItem()
|
||||
innerShedder.apply(shedPokemon)
|
||||
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
|
||||
pokemon.storeCoordinates.get()?.store?.add(shedPokemon)
|
||||
CobblemonCriteria.EVOLVE_POKEMON.trigger(owner, EvolvePokemonContext(pokemon.preEvolution!!.species.resourceIdentifier, shedPokemon.species.resourceIdentifier, playerData.get(owner).advancementData.totalEvolvedCount))
|
||||
// Consume one of the balls
|
||||
pokeballStack?.decrement(1)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts this evolution as soon as possible.
|
||||
@@ -155,6 +197,7 @@ interface Evolution : EvolutionLike {
|
||||
// we want to instantly tick for example you might only evolve your Bulbasaur at level 34 so Venusaur should be immediately available
|
||||
pokemon.lockedEvolutions.filterIsInstance<PassiveEvolution>().forEach { evolution -> evolution.attemptEvolution(pokemon) }
|
||||
pokemon.getOwnerPlayer()?.playSound(CobblemonSounds.EVOLVING, SoundCategory.NEUTRAL, 1F, 1F)
|
||||
this.shed(pokemon)
|
||||
CobblemonEvents.EVOLUTION_COMPLETE.post(EvolutionCompleteEvent(pokemon, this))
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
val level = buffer.readSizedInt(IntSize.U_SHORT)
|
||||
val moves = mutableListOf<MoveTemplate>()
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
|
||||
repeat(times = buffer.readSizedInt(IntSize.U_SHORT)) {
|
||||
Moves.getByNumericalId(buffer.readInt())?.let(moves::add)
|
||||
}
|
||||
levelUpMoves[level] = moves
|
||||
@@ -112,7 +112,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, levelUpMoves.size)
|
||||
for ((level, moves) in levelUpMoves) {
|
||||
buffer.writeSizedInt(IntSize.U_SHORT, level)
|
||||
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
|
||||
buffer.writeSizedInt(IntSize.U_SHORT, moves.size)
|
||||
for (move in moves) {
|
||||
buffer.writeInt(move.num)
|
||||
}
|
||||
|
||||
@@ -51,8 +51,8 @@ enum class Stats(override val identifier: Identifier, override val displayName:
|
||||
|
||||
/** Gets the [Stat] from the respective Showdown id. */
|
||||
fun getStat(statKey: String) = when(statKey) {
|
||||
"atk" -> ATTACK
|
||||
"def" -> DEFENCE
|
||||
"atk", "Attack" -> ATTACK // Hyper Cutter states the full stat name "Attack"
|
||||
"def", "Defense" -> DEFENCE // Big Pecks states the full stat name "Defense"
|
||||
"spa" -> SPECIAL_ATTACK
|
||||
"spd" -> SPECIAL_DEFENCE
|
||||
"spe" -> SPEED
|
||||
|
||||
@@ -8,6 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.pokemon.status
|
||||
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectTimeline
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
@@ -20,4 +23,6 @@ open class Status(
|
||||
val showdownName: String = "",
|
||||
val applyMessage: String,
|
||||
val removeMessage: String
|
||||
)
|
||||
) {
|
||||
fun getActionEffect() = ActionEffects.actionEffects[name]
|
||||
}
|
||||
@@ -9,21 +9,29 @@
|
||||
package com.cobblemon.mod.common.api.pokemon.status
|
||||
|
||||
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.BurnStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.FrozenStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.ParalysisStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonBadlyStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.PoisonStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.SleepStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.VolatileStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.BurnStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.FrozenStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.ParalysisStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonBadlyStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.SleepStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.nonpersistent.ConfuseStatus
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.nonpersistent.AttractStatus
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Main API point for Statuses
|
||||
* Get or register Statuses
|
||||
*
|
||||
* NOTE: May seem weird to have so many things called volatile statuses but the package is called nonpersistent.
|
||||
* Its because volatile is a reserved keyword in Java. Cant use it in a package name
|
||||
*
|
||||
* @author Deltric
|
||||
*/
|
||||
object Statuses {
|
||||
private val persistentStatuses = mutableListOf<Status>()
|
||||
private val volatileStatuses = mutableListOf<Status>()
|
||||
private val allStatuses = mutableListOf<Status>()
|
||||
|
||||
val POISON = registerStatus(PoisonStatus())
|
||||
@@ -32,13 +40,23 @@ object Statuses {
|
||||
val SLEEP = registerStatus(SleepStatus())
|
||||
val FROZEN = registerStatus(FrozenStatus())
|
||||
val BURN = registerStatus(BurnStatus())
|
||||
val ATTRACT = registerStatus(AttractStatus())
|
||||
val CONFUSE = registerStatus(ConfuseStatus())
|
||||
|
||||
|
||||
|
||||
fun <T: Status> registerStatus(status: T) : T {
|
||||
if (status is PersistentStatus) {
|
||||
persistentStatuses.add(status)
|
||||
}
|
||||
else if (status is VolatileStatus) {
|
||||
volatileStatuses.add(status)
|
||||
}
|
||||
allStatuses.add(status)
|
||||
return status
|
||||
}
|
||||
|
||||
fun getStatus(name: Identifier) = allStatuses.find { status -> status.name == name }
|
||||
fun getStatus(showdownName: String) = allStatuses.find { it.showdownName == showdownName }
|
||||
fun getPersistentStatuses() = allStatuses.filterIsInstance<PersistentStatus>()
|
||||
fun getPersistentStatuses() = persistentStatuses
|
||||
}
|
||||
@@ -79,15 +79,15 @@ class ParticleEvent(
|
||||
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.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.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 } }
|
||||
}
|
||||
|
||||
@@ -15,10 +15,12 @@ import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.codec.CodecMapped
|
||||
import com.cobblemon.mod.common.api.data.ArbitrarilyMappedSerializableCompanion
|
||||
import com.cobblemon.mod.common.client.particle.ParticleStorm
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.util.codec.EXPRESSION_CODEC
|
||||
import com.cobblemon.mod.common.util.getString
|
||||
import com.cobblemon.mod.common.util.math.geometry.transformDirection
|
||||
import com.cobblemon.mod.common.util.resolveDouble
|
||||
import com.cobblemon.mod.common.util.resolveVec3d
|
||||
import com.mojang.serialization.Codec
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import com.mojang.serialization.codecs.PrimitiveCodec
|
||||
@@ -35,20 +37,79 @@ interface ParticleMotion : CodecMapped {
|
||||
init {
|
||||
registerSubtype(ParticleMotionType.DYNAMIC, DynamicParticleMotion::class.java, DynamicParticleMotion.CODEC)
|
||||
registerSubtype(ParticleMotionType.STATIC, StaticParticleMotion::class.java, StaticParticleMotion.CODEC)
|
||||
registerSubtype(ParticleMotionType.PARAMETRIC, ParametricParticleMotion::class.java, ParametricParticleMotion.CODEC)
|
||||
}
|
||||
}
|
||||
|
||||
val type: ParticleMotionType
|
||||
fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d): Vec3d
|
||||
fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d
|
||||
fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d
|
||||
fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float): Vec3d
|
||||
}
|
||||
|
||||
enum class ParticleMotionType {
|
||||
DYNAMIC,
|
||||
PARAMETRIC, // TODO figure out how that even works
|
||||
PARAMETRIC,
|
||||
STATIC
|
||||
}
|
||||
|
||||
class ParametricParticleMotion(
|
||||
var offset: Triple<Expression, Expression, Expression> = Triple(
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0)
|
||||
),
|
||||
var direction: Triple<Expression, Expression, Expression> = Triple(
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0),
|
||||
NumberExpression(0.0)
|
||||
)
|
||||
) : ParticleMotion {
|
||||
companion object {
|
||||
val CODEC: Codec<ParametricParticleMotion> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
|
||||
EXPRESSION_CODEC.listOf().fieldOf("offset").forGetter { listOf(it.offset.first, it.offset.second, it.offset.third) },
|
||||
EXPRESSION_CODEC.listOf().fieldOf("direction").forGetter { listOf(it.direction.first, it.direction.second, it.direction.third) }
|
||||
).apply(instance) { _, offset, direction -> ParametricParticleMotion(Triple(offset[0], offset[1], offset[2]), Triple(direction[0], direction[1], direction[2])) }
|
||||
}
|
||||
}
|
||||
|
||||
override val type = ParticleMotionType.PARAMETRIC
|
||||
|
||||
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
|
||||
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d {
|
||||
val stormPosition = Vec3d(particle.storm.getX(), particle.storm.getY(), particle.storm.getZ())
|
||||
val offset = runtime.resolveVec3d(this.offset)
|
||||
val particlePosition = Vec3d(particle.getX(), particle.getY(), particle.getZ())
|
||||
val desiredPosition = stormPosition.add(offset)
|
||||
return desiredPosition.subtract(particlePosition)
|
||||
}
|
||||
|
||||
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = runtime.resolveVec3d(direction).normalize()
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
offset = Triple(
|
||||
MoLang.createParser(buffer.readString()).parseExpression(),
|
||||
MoLang.createParser(buffer.readString()).parseExpression(),
|
||||
MoLang.createParser(buffer.readString()).parseExpression()
|
||||
)
|
||||
direction = Triple(
|
||||
MoLang.createParser(buffer.readString()).parseExpression(),
|
||||
MoLang.createParser(buffer.readString()).parseExpression(),
|
||||
MoLang.createParser(buffer.readString()).parseExpression()
|
||||
)
|
||||
}
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeString(offset.first.getString())
|
||||
buffer.writeString(offset.second.getString())
|
||||
buffer.writeString(offset.third.getString())
|
||||
buffer.writeString(direction.first.getString())
|
||||
buffer.writeString(direction.second.getString())
|
||||
buffer.writeString(direction.third.getString())
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicParticleMotion(
|
||||
var direction: ParticleMotionDirection = InwardsMotionDirection(),
|
||||
var speed: Expression = NumberExpression(0.0),
|
||||
@@ -79,14 +140,23 @@ class DynamicParticleMotion(
|
||||
return direction.getDirectionVector(runtime, storm, emitterPos, particlePos).normalize().multiply(runtime.resolveDouble(speed))
|
||||
}
|
||||
|
||||
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d): Vec3d {
|
||||
return Vec3d(
|
||||
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d): Vec3d {
|
||||
val acceleration = Vec3d(
|
||||
runtime.resolveDouble(acceleration.first),
|
||||
runtime.resolveDouble(acceleration.second),
|
||||
runtime.resolveDouble(acceleration.third)
|
||||
).subtract(velocity.multiply(runtime.resolveDouble(drag)))
|
||||
)
|
||||
.subtract(velocity.multiply(20 * runtime.resolveDouble(drag)))
|
||||
.multiply(1 / 20.0).multiply(1 / 20.0) // blocks per second per second -> blocks per tick per tick
|
||||
|
||||
return Vec3d(
|
||||
velocity.x + acceleration.x,
|
||||
velocity.y + acceleration.y,
|
||||
velocity.z + acceleration.z
|
||||
)
|
||||
}
|
||||
|
||||
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = velocity.normalize()
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
@@ -234,7 +304,8 @@ class StaticParticleMotion : ParticleMotion {
|
||||
override val type = ParticleMotionType.STATIC
|
||||
|
||||
override fun getInitialVelocity(runtime: MoLangRuntime, storm: ParticleStorm, particlePos: Vec3d, emitterPos: Vec3d) = Vec3d.ZERO
|
||||
override fun getAcceleration(runtime: MoLangRuntime, velocity: Vec3d) = Vec3d.ZERO
|
||||
override fun getVelocity(runtime: MoLangRuntime, particle: SnowstormParticle, velocity: Vec3d) = velocity
|
||||
override fun getParticleDirection(runtime: MoLangRuntime, storm: ParticleStorm, velocity: Vec3d, minSpeed: Float) = velocity.normalize()
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {}
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {}
|
||||
|
||||
@@ -32,6 +32,7 @@ interface ParticleRotation : CodecMapped {
|
||||
) {
|
||||
init {
|
||||
registerSubtype(ParticleRotationType.DYNAMIC, DynamicParticleRotation::class.java, DynamicParticleRotation.CODEC)
|
||||
registerSubtype(ParticleRotationType.PARAMETRIC, ParametricParticleRotation::class.java, ParametricParticleRotation.CODEC)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +40,34 @@ interface ParticleRotation : CodecMapped {
|
||||
|
||||
fun getInitialRotation(runtime: MoLangRuntime): Double
|
||||
fun getInitialAngularVelocity(runtime: MoLangRuntime): Double
|
||||
fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double
|
||||
fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double
|
||||
}
|
||||
|
||||
class ParametricParticleRotation(var expression: Expression = NumberExpression(0.0)): ParticleRotation {
|
||||
companion object {
|
||||
val CODEC: Codec<ParametricParticleRotation> = RecordCodecBuilder.create { instance ->
|
||||
instance.group(
|
||||
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
|
||||
EXPRESSION_CODEC.fieldOf("expression").forGetter { it.expression }
|
||||
).apply(instance) { _, expression ->
|
||||
ParametricParticleRotation(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val type = ParticleRotationType.PARAMETRIC
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(expression)
|
||||
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = 0.0
|
||||
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double) = runtime.resolveDouble(expression) - angle
|
||||
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
expression = MoLang.createParser(buffer.readString()).parseExpression()
|
||||
}
|
||||
|
||||
override fun writeToBuffer(buffer: PacketByteBuf) {
|
||||
buffer.writeString(expression.getString())
|
||||
}
|
||||
}
|
||||
|
||||
class DynamicParticleRotation(
|
||||
@@ -66,15 +94,15 @@ class DynamicParticleRotation(
|
||||
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
|
||||
override fun getInitialRotation(runtime: MoLangRuntime) = runtime.resolveDouble(startRotation)
|
||||
override fun getInitialAngularVelocity(runtime: MoLangRuntime) = runtime.resolveDouble(speed) / 20
|
||||
override fun getAngularAcceleration(runtime: MoLangRuntime, angularVelocity: Double): Double {
|
||||
override fun getAngularVelocity(runtime: MoLangRuntime, angle: Double, angularVelocity: Double): Double {
|
||||
val acceleration = runtime.resolveDouble(acceleration)
|
||||
val nextVelocity = angularVelocity + acceleration
|
||||
val nextVelocity = angularVelocity * 20 + acceleration
|
||||
val drag = nextVelocity * runtime.resolveDouble(drag)
|
||||
return if (abs(drag) > abs(nextVelocity)) {
|
||||
return angularVelocity + (if (abs(drag) > abs(nextVelocity)) {
|
||||
0.0
|
||||
} else {
|
||||
nextVelocity - drag - angularVelocity
|
||||
}
|
||||
nextVelocity - drag - angularVelocity * 20
|
||||
})
|
||||
}
|
||||
|
||||
override fun readFromBuffer(buffer: PacketByteBuf) {
|
||||
|
||||
@@ -10,12 +10,7 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.api.entity.Despawner
|
||||
import com.cobblemon.mod.common.api.spawning.condition.AreaSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.BasicSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.GroundedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
|
||||
import com.cobblemon.mod.common.api.spawning.condition.*
|
||||
import com.cobblemon.mod.common.api.spawning.context.AreaContextResolver
|
||||
import com.cobblemon.mod.common.api.spawning.context.GroundedSpawningContext
|
||||
import com.cobblemon.mod.common.api.spawning.context.LavafloorSpawningContext
|
||||
@@ -91,6 +86,7 @@ object BestSpawner {
|
||||
SpawningCondition.register(SubmergedSpawningCondition.NAME, SubmergedSpawningCondition::class.java)
|
||||
SpawningCondition.register(GroundedSpawningCondition.NAME, GroundedSpawningCondition::class.java)
|
||||
SpawningCondition.register(SurfaceSpawningCondition.NAME, SurfaceSpawningCondition::class.java)
|
||||
SpawningCondition.register(SeafloorSpawningCondition.NAME, SeafloorSpawningCondition::class.java)
|
||||
|
||||
LOGGER.info("Loaded ${SpawningCondition.conditionTypes.size} spawning condition types.")
|
||||
SpawningContextCalculator.register(GroundedSpawningContextCalculator)
|
||||
@@ -100,7 +96,7 @@ object BestSpawner {
|
||||
SpawningContextCalculator.register(SurfaceSpawningContextCalculator)
|
||||
|
||||
SpawningContext.register(name = "grounded", clazz = GroundedSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "seafloor", clazz = SeafloorSpawningContext::class.java, defaultCondition = SeafloorSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "lavafloor", clazz = LavafloorSpawningContext::class.java, defaultCondition = GroundedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "submerged", clazz = SubmergedSpawningContext::class.java, defaultCondition = SubmergedSpawningCondition.NAME)
|
||||
SpawningContext.register(name = "surface", clazz = SurfaceSpawningContext::class.java, defaultCondition = SurfaceSpawningCondition.NAME)
|
||||
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.condition
|
||||
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.spawning.context.SeafloorSpawningContext
|
||||
import com.cobblemon.mod.common.util.Merger
|
||||
import net.minecraft.block.Block
|
||||
|
||||
/**
|
||||
* Base type for a spawning condition that applies to some kind of [SeafloorSpawningContext]. This
|
||||
* can be extended for subclasses of [SeafloorSpawningContext].
|
||||
*
|
||||
* Borrowed the Grounded context code since it was unintentionally spawning Pokémon on the sea floor
|
||||
* already. Seems to be working fine /shrug
|
||||
*
|
||||
* @author FrankTheFarmer
|
||||
* @since May 22nd, 2024
|
||||
*/
|
||||
abstract class SeafloorTypeSpawningCondition<T : SeafloorSpawningContext> : AreaTypeSpawningCondition<T>() {
|
||||
var neededBaseBlocks: MutableList<RegistryLikeCondition<Block>>? = null
|
||||
|
||||
override fun fits(ctx: T): Boolean {
|
||||
return if (!super.fits(ctx)) {
|
||||
false
|
||||
} else if (minHeight != null && ctx.height < minHeight!!) {
|
||||
return false
|
||||
} else if (maxHeight != null && ctx.height > maxHeight!!) {
|
||||
return false
|
||||
} else !(neededBaseBlocks != null && neededBaseBlocks!!.none { it.fits(ctx.baseBlock.block, ctx.blockRegistry) })
|
||||
}
|
||||
|
||||
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
|
||||
super.copyFrom(other, merger)
|
||||
if (other is SeafloorTypeSpawningCondition) {
|
||||
neededBaseBlocks = merger.merge(neededBaseBlocks, other.neededBaseBlocks)?.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A spawning condition for a [SeafloorSpawningContext].
|
||||
*
|
||||
* @author FrankTheFarmer
|
||||
* @since May 22nd, 2024
|
||||
*/
|
||||
open class SeafloorSpawningCondition : SeafloorTypeSpawningCondition<SeafloorSpawningContext>() {
|
||||
override fun contextClass() = SeafloorSpawningContext::class.java
|
||||
companion object {
|
||||
const val NAME = "seafloor"
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -49,7 +49,7 @@ open class AreaSpawningContext(
|
||||
* This is not considering the provided state as what the entity would be on top of,
|
||||
* but rather the space its hitbox would fill.
|
||||
*/
|
||||
open fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState): Boolean = !state.isFullCube(world, pos)
|
||||
open fun isSafeSpace(world: ServerWorld, pos: BlockPos, state: BlockState): Boolean = !state.isSolid
|
||||
|
||||
override fun postFilter(detail: SpawnDetail): Boolean {
|
||||
if (!super.postFilter(detail)) {
|
||||
|
||||
+2
-1
@@ -34,7 +34,8 @@ class BestSpawnerConfig {
|
||||
val contextWeights = mutableMapOf(
|
||||
"grounded" to 1F,
|
||||
"submerged" to 0.99F,
|
||||
"surface" to 0.01F
|
||||
"surface" to 0.01F,
|
||||
"seafloor" to 1F
|
||||
)
|
||||
val buckets = mutableListOf(
|
||||
SpawnBucket("common", 93.8F),
|
||||
|
||||
@@ -24,6 +24,7 @@ object CobblemonBlockTags {
|
||||
|
||||
@JvmField val ALL_HANGING_SIGNS = createTag("all_hanging_signs")
|
||||
@JvmField val ALL_SIGNS = createTag("all_signs")
|
||||
@JvmField val ALL_SAPLINGS = createTag("all_saplings")
|
||||
@JvmField val APRICORN_LEAVES = createTag("apricorn_leaves")
|
||||
@JvmField val APRICORN_LOGS = createTag("apricorn_logs")
|
||||
@JvmField val APRICORN_SAPLINGS = createTag("apricorn_saplings")
|
||||
@@ -37,6 +38,7 @@ object CobblemonBlockTags {
|
||||
@JvmField val DRIPSTONE_REPLACEABLES = createTag("dripstone_replaceables")
|
||||
@JvmField val FLOWERS = createTag("flowers")
|
||||
@JvmField val MEDICINAL_LEEK_PLANTABLE = createTag("medicinal_leek_plantable")
|
||||
@JvmField val BUGWORT_PLANTABLE = createTag("bugwort_plantable")
|
||||
@JvmField val MINTS = createTag("mints")
|
||||
@JvmField val ROOTS_SPREADABLE = createTag("roots_spreadable")
|
||||
@JvmField val SIGNS = createTag("signs")
|
||||
@@ -44,6 +46,9 @@ object CobblemonBlockTags {
|
||||
@JvmField val SEES_SKY = createTag("sees_sky")
|
||||
@JvmField val SNOW_BLOCK = createTag("snow_block")
|
||||
@JvmField val ROOTS = createTag("roots")
|
||||
@JvmField val SACCHARINE_LEAVES = createTag("saccharine_leaves")
|
||||
@JvmField val SACCHARINE_LOGS = createTag("saccharine_logs")
|
||||
@JvmField val SACCHARINE_SAPLING = createTag("saccharine_sapling")
|
||||
@JvmField val STANDING_SIGNS = createTag("standing_signs")
|
||||
@JvmField val TUMBLESTONE_HEAT_SOURCE = createTag("tumblestone_heat_source")
|
||||
@JvmField val WALL_HANGING_SIGNS = createTag("wall_hanging_signs")
|
||||
|
||||
+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.battles.dispatch
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* An [InterpreterInstruction] that triggers an [ActionEffectTimeline]
|
||||
* I think it might be better to have this as an abstract class so we can default the fields
|
||||
*
|
||||
* @author Apion
|
||||
* @since May 19, 2024
|
||||
*/
|
||||
interface ActionEffectInstruction : InterpreterInstruction {
|
||||
var future: CompletableFuture<*>
|
||||
var holds: MutableSet<String>
|
||||
//To expose via molang, so action effects can do different stuff in different instructions
|
||||
//e.g. "x is confused" vs "x hit itself in confusion"
|
||||
val id: Identifier
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
preActionEffect(battle)
|
||||
val runtime = MoLangRuntime()
|
||||
battle.addQueryFunctions(runtime.environment.getQueryStruct())
|
||||
runtime.environment.getQueryStruct().addStandardFunctions()
|
||||
addMolangQueries(runtime)
|
||||
runActionEffect(battle, runtime)
|
||||
postActionEffect(battle)
|
||||
}
|
||||
|
||||
fun preActionEffect(battle: PokemonBattle)
|
||||
fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime)
|
||||
fun postActionEffect(battle: PokemonBattle)
|
||||
|
||||
fun addMolangQueries(runtime: MoLangRuntime) {
|
||||
runtime.environment.getQueryStruct()
|
||||
.addFunction("instruction_id") { StringValue(id.toString()) }
|
||||
}
|
||||
|
||||
}
|
||||
+50
-6
@@ -8,16 +8,28 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.CauserInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |-activate|POKEMON|EFFECT
|
||||
@@ -28,19 +40,50 @@ import net.minecraft.text.Text
|
||||
* @author Hunter
|
||||
* @since September 25th, 2022
|
||||
*/
|
||||
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : InterpreterInstruction, CauserInstruction {
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
class ActivateInstruction(val instructionSet: InstructionSet, val message: BattleMessage) : ActionEffectInstruction, CauserInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("activate")
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val effect = message.effectAt(1) ?: return
|
||||
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemon)
|
||||
|
||||
battle.dispatch{
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
ShowdownInterpreter.lastCauser[battle.battleId] = message
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
battle.dispatch {
|
||||
val effect = message.effectAt(1) ?: return@dispatch GO
|
||||
val status = Statuses.getStatus(effect.id)
|
||||
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
future.thenApply { holds.clear() }
|
||||
return@dispatch GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
battle.dispatch {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
val extraEffect = message.effectAt(2)?.typelessData ?: Text.literal("UNKNOWN")
|
||||
val effect = message.effectAt(1) ?: return@dispatch GO
|
||||
val pokemonName = pokemon.getName()
|
||||
val sourceName = message.battlePokemonFromOptional(battle)?.getName() ?: Text.literal("UNKNOWN")
|
||||
val lang = when (effect.id) {
|
||||
// Includes a 3rd argument being the magnitude level as a number
|
||||
"magnitude" -> battleLang("activate.magnitude", message.argumentAt(2)?.toIntOrNull() ?: 1)
|
||||
@@ -59,7 +102,8 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
|
||||
else -> battleLang("activate.${effect.id}", pokemonName, sourceName, extraEffect)
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
WaitDispatch(1F)
|
||||
//We check holds here so the chat msg + particle effect happen more concurrently, instead of sequentially
|
||||
UntilDispatch { "effects" !in holds}
|
||||
}
|
||||
}
|
||||
}
|
||||
+45
-3
@@ -8,14 +8,25 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Format: |-boost|POKEMON|STAT|AMOUNT or |-unboost|POKEMON|STAT|AMOUNT
|
||||
@@ -25,9 +36,33 @@ import com.cobblemon.mod.common.util.battleLang
|
||||
* @author Hiroku
|
||||
* @since August 20th, 2022
|
||||
*/
|
||||
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): InterpreterInstruction {
|
||||
class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMessage, val remainingLines: Iterator<BattleMessage>, val isBoost: Boolean = true): ActionEffectInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("boost")
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
}
|
||||
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
battle.dispatch {
|
||||
val actionEffect = if (isBoost) BOOST_EFFECT else UNBOOST_EFFECT
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
future.thenApply { holds.clear() }
|
||||
return@dispatch GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val statKey = message.argumentAt(1) ?: return
|
||||
val stages = message.argumentAt(2)?.toInt() ?: return
|
||||
@@ -35,7 +70,7 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
|
||||
val severity = Stats.getSeverity(stages)
|
||||
val rootKey = if (isBoost) "boost" else "unboost"
|
||||
|
||||
battle.dispatchWaiting {
|
||||
battle.dispatch {
|
||||
val lang = when {
|
||||
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
|
||||
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
|
||||
@@ -47,6 +82,13 @@ class BoostInstruction(val instructionSet: InstructionSet, val message: BattleMe
|
||||
// TODO: replace with context that tracks detailed information such as # of stages
|
||||
repeat(stages) { pokemon.contextManager.add(context) }
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
return@dispatch UntilDispatch { "effects" !in holds }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val BOOST_EFFECT = ActionEffects.actionEffects[cobblemonResource("boost")]!!
|
||||
val UNBOOST_EFFECT = ActionEffects.actionEffects[cobblemonResource("unboost")]!!
|
||||
}
|
||||
|
||||
}
|
||||
+51
-6
@@ -8,14 +8,27 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.util.Identifier
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
* Formats: |cant|POKEMON|REASON and |cant|POKEMON|REASON|MOVE
|
||||
@@ -24,12 +37,40 @@ import com.cobblemon.mod.common.util.lang
|
||||
* @author Deltric
|
||||
* @since January 22nd, 2022
|
||||
*/
|
||||
class CantInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("cant")
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
}
|
||||
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
battle.dispatch {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
|
||||
val name = pokemon.getName()
|
||||
val status = Statuses.getStatus(effectID)
|
||||
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
pokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
future.thenApply { holds.clear() }
|
||||
GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
battle.dispatch {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
|
||||
val name = pokemon.getName()
|
||||
// Move may be null as it's not always given
|
||||
val moveName = message.moveAt(2)?.displayName ?: run { "(Unrecognized: ${message.argumentAt(2)})".text() }
|
||||
@@ -38,7 +79,7 @@ class CantInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
// TODO: in the games they use a generic image because there is a popup of the ability and the sprite of the mon, it may be good to have a similar system here
|
||||
"armortail", "damp", "dazzling", "queenlymajesty" -> battleLang("cant.generic", name, moveName)
|
||||
"par", "slp", "frz" -> {
|
||||
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatchWaiting
|
||||
val status = Statuses.getStatus(effectID)?.name?.path ?: return@dispatch GO
|
||||
lang("status.$status.is", name)
|
||||
}
|
||||
else -> battleLang("cant.$effectID", name, moveName)
|
||||
@@ -46,6 +87,10 @@ class CantInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
battle.broadcastChatMessage(lang.red())
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
UntilDispatch { "effects" !in holds }
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+74
-23
@@ -8,15 +8,21 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter.lastCauser
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InstructionSet
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
@@ -25,11 +31,13 @@ import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePac
|
||||
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.pokemon.status.statuses.persistent.PoisonStatus
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import kotlin.math.roundToInt
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
/**
|
||||
* Format: |-damage|POKEMON|HP STATUS
|
||||
@@ -43,45 +51,87 @@ class DamageInstruction(
|
||||
val actor: BattleActor,
|
||||
val publicMessage: BattleMessage,
|
||||
val privateMessage: BattleMessage
|
||||
) : InterpreterInstruction {
|
||||
var future = CompletableFuture.completedFuture(Unit)
|
||||
) : ActionEffectInstruction {
|
||||
val expectedTarget = publicMessage.battlePokemon(0, actor.battle)
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("damage")
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
|
||||
val recoiling = privateMessage.optionalArgument("from")?.equals("recoil", true) == true
|
||||
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
|
||||
if (recoiling) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
if (RecoilEvolutionProgress.supports(pokemon)) {
|
||||
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException("Cant get recoil string")
|
||||
val newHealth = "([0-9]+).*".toRegex().find(healthStr)?.groups?.get(1)?.value?.toIntOrNull() ?: throw UnsupportedOperationException("Cant get recoil string")
|
||||
val difference = pokemon.currentHealth - newHealth
|
||||
if (difference > 0) {
|
||||
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
|
||||
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
|
||||
}
|
||||
doRecoilEvoChecks(battlePokemon)
|
||||
if (lastCauser is MoveInstruction) {
|
||||
battle.dispatch {
|
||||
//Is the recoil hold currently used anywhere?
|
||||
UntilDispatch { "recoil" !in lastCauser.holds }
|
||||
}
|
||||
}
|
||||
}
|
||||
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
|
||||
val effect = privateMessage.effect()
|
||||
val source = privateMessage.battlePokemonFromOptional(battle)
|
||||
var causedFaint = newHealth == "0"
|
||||
source?.let { ShowdownInterpreter.broadcastOptionalAbility(battle, effect, it) }
|
||||
}
|
||||
|
||||
battle.dispatch {
|
||||
if (lastCauser is MoveInstruction && recoiling) {
|
||||
UntilDispatch { "recoil" !in lastCauser.holds }
|
||||
} else {
|
||||
GO
|
||||
private fun doRecoilEvoChecks(battlePokemon: BattlePokemon) {
|
||||
battlePokemon.effectedPokemon.let { pokemon ->
|
||||
if (RecoilEvolutionProgress.supports(pokemon)) {
|
||||
val healthStr = privateMessage.argumentAt(1) ?: throw UnsupportedOperationException(
|
||||
"Cant get recoil string"
|
||||
)
|
||||
val newHealth =
|
||||
"([0-9]+).*".toRegex().find(healthStr)?.groups?.get(1)?.value?.toIntOrNull()
|
||||
?: throw UnsupportedOperationException("Cant get recoil string")
|
||||
val difference = pokemon.currentHealth - newHealth
|
||||
if (difference > 0) {
|
||||
val progress = pokemon.evolutionProxy.current()
|
||||
.progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
|
||||
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
source?.let { ShowdownInterpreter.broadcastOptionalAbility(battle, effect, it) }
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
val effect = privateMessage.effect()
|
||||
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
|
||||
var status = effect?.id?.let { Statuses.getStatus(it) }
|
||||
battle.dispatch {
|
||||
val pokemon = privateMessage.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
//Showdown doesnt tell us on damage if its poison or toxic so we gotta consult the entity
|
||||
if (status is PoisonStatus) {
|
||||
status = pokemon.effectedPokemon.status?.status ?: status
|
||||
}
|
||||
val actionEffect = status?.getActionEffect() ?: return@dispatch GO
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
battlePokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
future.thenApply { holds.clear() }
|
||||
return@dispatch GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
val newHealth = privateMessage.argumentAt(1)?.split(" ")?.get(0) ?: return
|
||||
val battlePokemon = publicMessage.battlePokemon(0, actor.battle) ?: return
|
||||
var causedFaint = newHealth == "0"
|
||||
val effect = privateMessage.effect()
|
||||
val source = privateMessage.battlePokemonFromOptional(battle)
|
||||
val lastCauser = instructionSet.getMostRecentCauser(comparedTo = this)
|
||||
battle.dispatch {
|
||||
val pokemonName = battlePokemon.getName()
|
||||
val pokemonEntity = battlePokemon.entity
|
||||
//Play recoil animation if the pokemon recoiling isnt dead
|
||||
if (!causedFaint && pokemonEntity != null) {
|
||||
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
|
||||
pkt.sendToPlayersAround(
|
||||
@@ -92,7 +142,7 @@ class DamageInstruction(
|
||||
distance = 50.0
|
||||
)
|
||||
}
|
||||
|
||||
//Play hit particle
|
||||
if (pokemonEntity != null) {
|
||||
RunPosableMoLangPacket(pokemonEntity.id, setOf("q.particle('cobblemon:hit', 'target')")).sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
@@ -157,8 +207,9 @@ class DamageInstruction(
|
||||
} else if (causedFaint) {
|
||||
GO
|
||||
} else {
|
||||
WaitDispatch(1F)
|
||||
UntilDispatch {"effects" !in holds}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+3
-1
@@ -10,10 +10,10 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.battleLang
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
/**
|
||||
@@ -121,6 +122,7 @@ class MoveInstruction(
|
||||
}
|
||||
|
||||
runtime.environment.getQueryStruct().addFunction("move") { move.struct }
|
||||
runtime.environment.getQueryStruct().addFunction("instruction_id") { StringValue(cobblemonResource("move").toString()) }
|
||||
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.api.mulch.Mulchable
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import dev.lambdaurora.lambdynlights.util.SodiumDynamicLightHandler.pos
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockRenderType
|
||||
import net.minecraft.block.BlockState
|
||||
@@ -87,22 +88,31 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
init {
|
||||
defaultState = this.stateManager.defaultState
|
||||
.with(WAS_GENERATED, false)
|
||||
.with(AGE, 0)
|
||||
.with(MULCH, MulchVariant.NONE)
|
||||
.with(AGE, 0)
|
||||
.with(IS_ROOTED, false)
|
||||
}
|
||||
|
||||
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
|
||||
growHelper(world, random, pos, state, true)
|
||||
}
|
||||
|
||||
//grow, but cooler
|
||||
fun growHelper(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState, boneMealed: Boolean = false) {
|
||||
val berry = berry() ?: return
|
||||
if (boneMealed && random.nextFloat() > berry.boneMealChance) return
|
||||
val curAge = state.get(AGE)
|
||||
val newAge = curAge + 1
|
||||
if (newAge > FRUIT_AGE) return
|
||||
val newState = state.with(AGE, newAge)
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (curAge == MATURE_AGE) {
|
||||
treeEntity.generateGrowthPoints(world, state, pos, null)
|
||||
determineMutation(world, random, pos, state)
|
||||
treeEntity.generateGrowthPoints(world, newState, pos, null)
|
||||
determineMutation(world, random, pos, newState)
|
||||
}
|
||||
|
||||
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
|
||||
convertMulchToEntity(world, newState, pos)
|
||||
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
|
||||
treeEntity.markDirty()
|
||||
}
|
||||
@@ -122,7 +132,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
|
||||
if (berryMutationOffer.mutations.isNotEmpty()) {
|
||||
var mutateChance = 125
|
||||
if (getMulch(state) == MulchVariant.SURPRISE) {
|
||||
if (getMulch(treeEntity) == MulchVariant.SURPRISE) {
|
||||
mutateChance *= 4
|
||||
treeEntity.decrementMulchDuration(world, pos, state)
|
||||
}
|
||||
@@ -143,7 +153,10 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
state: BlockState,
|
||||
variant: MulchVariant
|
||||
): Boolean {
|
||||
return getMulch(state) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && world.getBlockState(pos.down()).isOf(Blocks.FARMLAND)
|
||||
val underBlockState = world.getBlockState(pos.down())
|
||||
val validSoil = state.get(IS_ROOTED) || underBlockState.isIn(CobblemonBlockTags.BERRY_SOIL)
|
||||
val treeEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return false
|
||||
return getMulch(treeEntity) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && validSoil
|
||||
}
|
||||
|
||||
override fun applyMulch(
|
||||
@@ -154,18 +167,15 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
variant: MulchVariant
|
||||
) {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
setMulch(world, pos, state, variant)
|
||||
treeEntity.mulchDuration = variant.duration
|
||||
treeEntity.setMulch(variant, world, state, pos)
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS, 0.6F, 1F)
|
||||
treeEntity.refreshTimers(pos)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
|
||||
if (player.getStackInHand(hand).item is ShovelItem && getMulch(state) != MulchVariant.NONE) {
|
||||
setMulch(world, pos, state, MulchVariant.NONE)
|
||||
treeEntity.markDirty()
|
||||
if (player.getStackInHand(hand).item is ShovelItem && getMulch(treeEntity) != MulchVariant.NONE) {
|
||||
treeEntity.setMulch(MulchVariant.NONE, world, state, pos)
|
||||
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS, 0.6F, 1F)
|
||||
this.spawnBreakParticles(world, player, pos, state.with(AGE, 0))
|
||||
return ActionResult.SUCCESS
|
||||
@@ -189,7 +199,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
val below = world.getBlockState(pos.down())
|
||||
return (state.get(WAS_GENERATED) && below.isIn(CobblemonBlockTags.BERRY_WILD_SOIL))
|
||||
|| below.isIn(CobblemonBlockTags.BERRY_SOIL)
|
||||
|| below.block is FarmlandBlock
|
||||
|| state.get(IS_ROOTED)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
@@ -208,6 +218,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
builder.add(AGE)
|
||||
builder.add(WAS_GENERATED)
|
||||
builder.add(MULCH)
|
||||
builder.add(IS_ROOTED)
|
||||
}
|
||||
|
||||
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
|
||||
@@ -239,6 +250,7 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
|
||||
val MULCH: EnumProperty<MulchVariant> = EnumProperty.of("mulch", MulchVariant::class.java)
|
||||
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
|
||||
val IS_ROOTED: BooleanProperty = BooleanProperty.of("rooted")
|
||||
// val PLANTED_SHAPE = VoxelShapes.union(
|
||||
// VoxelShapes.cuboid(0.3125, -0.0625, 0.3125, 0.6875, 0.0, 0.6875),
|
||||
// VoxelShapes.cuboid(0.375, 0.0, 0.375, 0.625, 0.0625, 0.625)
|
||||
@@ -301,15 +313,14 @@ class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) :
|
||||
val TALL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
|
||||
|
||||
|
||||
fun getMulch(state: BlockState): MulchVariant {
|
||||
if (!state.contains(MULCH)) {
|
||||
return MulchVariant.NONE
|
||||
}
|
||||
return state.get(MULCH)
|
||||
}
|
||||
fun getMulch(entity: BerryBlockEntity) = entity.mulchVariant
|
||||
|
||||
fun setMulch(world: World, pos: BlockPos, state: BlockState, mulch: MulchVariant) {
|
||||
world.setBlockState(pos, state.with(MULCH, mulch))
|
||||
fun convertMulchToEntity(world: ServerWorld, state: BlockState, pos: BlockPos) {
|
||||
val entity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
|
||||
if (state.get(MULCH) != MulchVariant.NONE && state.get(MULCH) != entity.mulchVariant) {
|
||||
entity.mulchVariant = state.get(MULCH)
|
||||
world.setBlockState(pos, state.with(MULCH, MulchVariant.NONE))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
|
||||
import com.cobblemon.mod.common.item.berry.BerryItem
|
||||
import net.minecraft.block.AbstractBlock
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
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.World
|
||||
|
||||
/**
|
||||
* @author Apion
|
||||
* @since February 27, 2024
|
||||
*/
|
||||
class BindingSoilBlock(settings: Settings) : Block(settings) {
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
if (!world.isClient) {
|
||||
val itemStack = player.getStackInHand(hand)
|
||||
val item = itemStack.item
|
||||
if (item is BerryItem) {
|
||||
world.setBlockState(pos, item.block.defaultState.with(BerryBlock.IS_ROOTED, true))
|
||||
if (!player.isCreative) {
|
||||
itemStack.decrement(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* 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.CobblemonItems
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemConvertible
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.IntProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
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.random.Random
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldView
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class BugwortBlock(settings: Settings) : CropBlock(settings), Fertilizable {
|
||||
|
||||
override fun getAgeProperty(): IntProperty = AGE
|
||||
|
||||
override fun getMaxAge(): Int = MATURE_AGE
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(this.ageProperty)
|
||||
}
|
||||
|
||||
override fun getSeedsItem(): ItemConvertible = CobblemonItems.BUGWORT
|
||||
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AGE_TO_SHAPE[this.getAge(state)]
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
// This is specified as growing fast like sugar cane
|
||||
// They have 15 age stages until they grow upwards, this is an attempt at a chance based but likely event
|
||||
if (this.isMature(state) || random.nextInt(4) != 0) {
|
||||
return
|
||||
}
|
||||
this.applyGrowth(world, pos, state)
|
||||
}
|
||||
|
||||
// These 3 are still around for the sake of compatibility, vanilla won't trigger it but some mods might
|
||||
// We implement applyGrowth & getGrowthAmount for them
|
||||
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = !this.isMature(state)
|
||||
|
||||
override fun applyGrowth(world: World, pos: BlockPos, state: BlockState) {
|
||||
world.setBlockState(pos, state.with(this.ageProperty, (this.getAge(state) + 1).coerceAtMost(this.maxAge)), NOTIFY_LISTENERS)
|
||||
}
|
||||
|
||||
override fun canPlantOnTop(floor: BlockState, world: BlockView?, pos: BlockPos?): Boolean {
|
||||
return floor.isOf(Blocks.GRASS_BLOCK) || floor.isOf(Blocks.DIRT) || floor.isOf(Blocks.FARMLAND)
|
||||
}
|
||||
|
||||
override fun canPlaceAt(state: BlockState?, world: WorldView?, pos: BlockPos?): Boolean {
|
||||
val blockPos = pos!!.down()
|
||||
return canPlantOnTop(world!!.getBlockState(blockPos), world, blockPos)
|
||||
}
|
||||
|
||||
override fun onUse(
|
||||
state: BlockState?,
|
||||
world: World?,
|
||||
pos: BlockPos?,
|
||||
player: PlayerEntity?,
|
||||
hand: Hand?,
|
||||
hit: BlockHitResult?
|
||||
): ActionResult {
|
||||
if (state != null && world != null && pos != null) {
|
||||
// if bugwort is at max age then revert age to 0 and drop items
|
||||
if (state.get(AGE) == 3) {
|
||||
|
||||
world.setBlockState(pos, state.with(this.ageProperty, (0).coerceAtMost(this.maxAge)), NOTIFY_LISTENERS)
|
||||
|
||||
Block.dropStack(world, pos, ItemStack(CobblemonItems.BUGWORT, kotlin.random.Random.nextInt(2, 4)))
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
|
||||
override fun getGrowthAmount(world: World): Int = 1
|
||||
|
||||
companion object {
|
||||
|
||||
const val MATURE_AGE = 3
|
||||
val AGE: IntProperty = Properties.AGE_3
|
||||
val AGE_TO_SHAPE = arrayOf(
|
||||
createCuboidShape(0.0, 0.0, 0.0, 16.0, 2.0, 16.0),
|
||||
createCuboidShape(0.0, 0.0, 0.0, 16.0, 5.0, 16.0),
|
||||
createCuboidShape(0.0, 0.0, 0.0, 16.0, 8.0, 16.0),
|
||||
createCuboidShape(0.0, 0.0, 0.0, 16.0, 11.0, 16.0)
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
/*
|
||||
* 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.CookingPotBlockEntity
|
||||
import com.cobblemon.mod.common.gui.CookingPotScreenHandler
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
import net.minecraft.entity.ItemEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.fluid.FluidState
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.screen.NamedScreenHandlerFactory
|
||||
import net.minecraft.screen.ScreenHandlerContext
|
||||
import net.minecraft.screen.SimpleNamedScreenHandlerFactory
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.stat.Stats
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.BlockMirror
|
||||
import net.minecraft.util.BlockRotation
|
||||
import net.minecraft.util.Hand
|
||||
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
|
||||
|
||||
class CookingPotBlock(settings: Settings?) : BlockWithEntity(settings), Waterloggable, InventoryProvider {
|
||||
|
||||
companion object {
|
||||
val COOKING: BooleanProperty = BooleanProperty.of("cooking")
|
||||
val FACING = Properties.FACING
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
val WATERLOGGED = Properties.WATERLOGGED
|
||||
|
||||
private val NORTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0, 1.0, 0.3125, 0.9375),
|
||||
VoxelShapes.cuboid(0.0, 0.3125, 0.75, 1.0, 0.9375, 0.9375),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private val SOUTH_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0625, 1.0, 0.3125, 1.0),
|
||||
VoxelShapes.cuboid(0.0, 0.3125, 0.0625, 1.0, 0.9375, 0.25),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private val WEST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0, 0.0, 0.0, 0.9375, 0.3125, 1.0),
|
||||
VoxelShapes.cuboid(0.75, 0.3125, 0.0, 0.9375, 0.9375, 1.0),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private val EAST_OUTLINE = VoxelShapes.union(
|
||||
VoxelShapes.cuboid(0.0625, 0.0, 0.0, 1.0, 0.3125, 1.0),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0, 0.25, 0.9375, 1.0),
|
||||
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
|
||||
)
|
||||
|
||||
private val EMPTY_INVENTORY = object : SidedInventory {
|
||||
override fun clear() {}
|
||||
|
||||
override fun size(): Int = 0
|
||||
|
||||
override fun isEmpty(): Boolean = true
|
||||
|
||||
override fun getStack(slot: Int) = ItemStack.EMPTY
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int) = ItemStack.EMPTY
|
||||
|
||||
override fun removeStack(slot: Int) = ItemStack.EMPTY
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack?) {}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?) = false
|
||||
|
||||
override fun getAvailableSlots(side: Direction?) = IntArray(0)
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?) = false
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?) = false
|
||||
|
||||
override fun markDirty() {}
|
||||
}
|
||||
}
|
||||
|
||||
private val TITLE: Text = Text.of("Campfire Pot")
|
||||
|
||||
init {
|
||||
defaultState = this.stateManager.defaultState.with(FACING, Direction.NORTH)
|
||||
.with(WATERLOGGED, false)
|
||||
.with(COOKING, false)
|
||||
.with(TRIGGERED, false)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onUse(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
player: PlayerEntity,
|
||||
hand: Hand,
|
||||
hit: BlockHitResult
|
||||
): ActionResult {
|
||||
if (world.isClient) {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
player.openHandledScreen(state.createScreenHandlerFactory(world, pos))
|
||||
player.incrementStat(Stats.INTERACT_WITH_CRAFTING_TABLE)
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun createScreenHandlerFactory(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos
|
||||
): NamedScreenHandlerFactory {
|
||||
return SimpleNamedScreenHandlerFactory({ syncId: Int, inventory: PlayerInventory, player: PlayerEntity ->
|
||||
CookingPotScreenHandler(syncId, inventory, ScreenHandlerContext.create(world, pos))
|
||||
}, TITLE)
|
||||
}
|
||||
|
||||
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
|
||||
return CookingPotBlockEntity(pos, state)
|
||||
}
|
||||
|
||||
override fun getInventory(state: BlockState, world: WorldAccess, pos: BlockPos): SidedInventory {
|
||||
val cookingPotBlockEntity = world.getBlockEntity(pos) as? CookingPotBlockEntity
|
||||
?: return EMPTY_INVENTORY
|
||||
return cookingPotBlockEntity.cookingPotInventory
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun canPathfindThrough(
|
||||
blockState: BlockState,
|
||||
blockGetter: BlockView,
|
||||
blockPos: BlockPos,
|
||||
pathComputationType: NavigationType
|
||||
) = false
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun hasComparatorOutput(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, WATERLOGGED, COOKING, TRIGGERED)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun rotate(blockState: BlockState, rotation: BlockRotation): BlockState {
|
||||
return blockState.with(FACING, rotation.rotate(blockState[FACING]))
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun mirror(blockState: BlockState, mirror: BlockMirror): BlockState {
|
||||
return blockState.rotate(mirror.getRotation(blockState[FACING]))
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onStateReplaced(
|
||||
state: BlockState,
|
||||
world: World,
|
||||
pos: BlockPos,
|
||||
newState: BlockState,
|
||||
moved: Boolean
|
||||
) {
|
||||
if (!state.isOf(newState.block)) {
|
||||
super.onStateReplaced(state, world, pos, newState, moved)
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun scheduledTick(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: net.minecraft.util.math.random.Random?) {
|
||||
val inventory = (world!!.getBlockEntity(pos) as CookingPotBlockEntity).cookingPotInventory
|
||||
val cookingPotEntity = (world.getBlockEntity(pos) as CookingPotBlockEntity)
|
||||
|
||||
if(world == null || pos == null) {
|
||||
return
|
||||
}
|
||||
//val tankEntity = world.getBlockEntity(pos) as MultiblockEntity
|
||||
|
||||
if (cookingPotEntity.blockState.get(COOKING)) {
|
||||
//print("TMM is in use so scheduled Tick is disabled")
|
||||
return
|
||||
}
|
||||
|
||||
/*//this.onTriggerEvent(state, world, pos, random)
|
||||
// todo if the output slot is empty then try to craft a TM to that slot
|
||||
if (ItemStack.areItemsEqual(inventory.items!!.get(3), ItemStack.EMPTY)) {
|
||||
if (state?.let { getInventory(it, world, pos) } != null) {
|
||||
val itemStack: ItemStack
|
||||
itemStack = if (inventory.filterTM != null)
|
||||
inventory.filterTM!!.copy()
|
||||
else
|
||||
ItemStack(CobblemonItems.BLANK_TM, 1)
|
||||
val tm = TechnicalMachines.getTechnicalMachineFromStack(itemStack)
|
||||
// todo use isReadyToCraftTM to finalize the creation
|
||||
if (((inventory.filterTM != null && tm != null) && isReadyToCraftTM(state, world, pos, tm)) || isReadyToCraftBlankTM(state, world, pos)) {
|
||||
|
||||
// create TM item to the output slot
|
||||
inventory.items!!.set(3, itemStack)
|
||||
cookingPotEntity.markDirty()
|
||||
|
||||
// Play sound for TM creation
|
||||
world.playSoundServer(cookingPotEntity.blockPos.toVec3d(), CobblemonSounds.TMM_CRAFT, SoundCategory.BLOCKS)
|
||||
}
|
||||
}
|
||||
}*/
|
||||
// todo if there is an item in the output slot then spit it out
|
||||
else {
|
||||
if (state?.let { getInventory(it, world, pos) } != null) {
|
||||
|
||||
val itemStack = inventory.items!!.get(3)
|
||||
|
||||
// Get the direction the block is facing
|
||||
val facingDirection = state.get(Properties.FACING)?.opposite ?: return
|
||||
|
||||
// Calculate the center position of the block
|
||||
val frontOffset = 0.5 // Half block offset to the front
|
||||
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
|
||||
// Create the ItemEntity at the center of the block
|
||||
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, itemStack).copy()
|
||||
|
||||
// Create the ItemEntity
|
||||
itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
|
||||
// Add the ItemEntity to the world
|
||||
world.spawnEntity(itemEntity)
|
||||
|
||||
//clear inventory of SidedInventory
|
||||
inventory.items?.clear()
|
||||
cookingPotEntity.markDirty()
|
||||
//getInventory(state, world, pos).clear()
|
||||
}
|
||||
}
|
||||
|
||||
/*if (state?.let { getInventory(it, world, pos) } != null) {
|
||||
val itemStack: ItemStack
|
||||
itemStack = if (inventory.filterTM != null)
|
||||
inventory.filterTM!!
|
||||
else
|
||||
ItemStack(CobblemonItems.BLANK_TM, 1)
|
||||
val tm = TechnicalMachines.getTechnicalMachineFromStack(itemStack)
|
||||
// todo use isReadyToCraftTM to finalize the creation
|
||||
if ((inventory.filterTM != null && isReadyToCraftTM(state, world, pos, tm!!)) || isReadyToCraftBlankTM(state, world, pos)) {
|
||||
// Get the direction the block is facing
|
||||
val facingDirection = state.get(Properties.FACING)?.opposite ?: return
|
||||
|
||||
// Calculate the center position of the block
|
||||
val frontOffset = 0.5 // Half block offset to the front
|
||||
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
|
||||
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
|
||||
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
|
||||
|
||||
// Create the ItemEntity at the center of the block
|
||||
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, itemStack).copy()
|
||||
|
||||
// Create the ItemEntity
|
||||
itemEntity.setVelocity(0.0, 0.0, 0.0)
|
||||
|
||||
// Add the ItemEntity to the world
|
||||
world.spawnEntity(itemEntity)
|
||||
|
||||
//clear inventory of SidedInventory
|
||||
inventory.items?.clear()
|
||||
//getInventory(state, world, pos).clear()
|
||||
}
|
||||
}*/
|
||||
}override fun getRenderType(blockState: BlockState): BlockRenderType {
|
||||
return BlockRenderType.MODEL
|
||||
}
|
||||
|
||||
override fun getFluidState(state: BlockState): FluidState {
|
||||
return if (state[WATERLOGGED]) {
|
||||
Fluids.WATER.getStill(false)
|
||||
} else super.getFluidState(state)
|
||||
}
|
||||
|
||||
override fun getOutlineShape(
|
||||
state: BlockState,
|
||||
world: BlockView,
|
||||
pos: BlockPos,
|
||||
context: ShapeContext
|
||||
): VoxelShape {
|
||||
return when (state[FACING]) {
|
||||
Direction.NORTH -> NORTH_OUTLINE
|
||||
Direction.SOUTH -> SOUTH_OUTLINE
|
||||
Direction.WEST -> WEST_OUTLINE
|
||||
else -> EAST_OUTLINE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ 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.inventory.SimpleInventory
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
@@ -209,9 +210,8 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
|
||||
): SidedInventory {
|
||||
val tankEntity =
|
||||
(if (state.get(PART) == TankPart.TOP) world.getBlockEntity(pos.down())
|
||||
else world.getBlockEntity(pos)) as RestorationTankBlockEntity
|
||||
|
||||
return tankEntity.inv
|
||||
else world.getBlockEntity(pos))
|
||||
return if(tankEntity != null && tankEntity is RestorationTankBlockEntity) tankEntity.inv else DummyInventory()
|
||||
}
|
||||
|
||||
enum class TankPart(private val label: String) : StringIdentifiable {
|
||||
@@ -229,5 +229,18 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
|
||||
val PART = EnumProperty.of("part", TankPart::class.java)
|
||||
val TRIGGERED = Properties.TRIGGERED
|
||||
val ON = BooleanProperty.of("on")
|
||||
class DummyInventory : SimpleInventory(0), SidedInventory {
|
||||
override fun getAvailableSlots(side: Direction): IntArray {
|
||||
return IntArray(0)
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack, dir: Direction?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack, dir: Direction): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,256 @@
|
||||
/*
|
||||
* 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.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.SaccharineStrippedLogBlock.Companion
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.block.BeehiveBlock.HONEY_LEVEL
|
||||
import net.minecraft.entity.*
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.attribute.DefaultAttributeContainer
|
||||
import net.minecraft.entity.attribute.EntityAttributes
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.fluid.Fluids
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.registry.tag.BlockTags
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.IntProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
import net.minecraft.util.ActionResult
|
||||
import net.minecraft.util.Arm
|
||||
import net.minecraft.util.Hand
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
import net.minecraft.util.math.BlockPos
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.MathHelper
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.util.shape.VoxelShape
|
||||
import net.minecraft.util.shape.VoxelShapes
|
||||
import net.minecraft.world.*
|
||||
|
||||
@Suppress("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
class SaccharineLeafBlock(settings: Settings) : LeavesBlock(settings), Fertilizable {
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(AGE, MIN_AGE)
|
||||
.with(DISTANCE, DISTANCE_MAX)
|
||||
.with(PERSISTENT, false)
|
||||
.with(Properties.WATERLOGGED, false)
|
||||
}
|
||||
|
||||
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
val blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
return if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
blockState.with(PERSISTENT, true).with(Properties.WATERLOGGED, worldView.getFluidState(blockPos).isOf(Fluids.WATER))
|
||||
} else null
|
||||
}
|
||||
|
||||
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState? {
|
||||
return if (!state.canPlaceAt(world, pos)) {
|
||||
Blocks.AIR.defaultState
|
||||
} else {
|
||||
super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hasRandomTicks(state: BlockState) = state.get(AGE) != 2
|
||||
|
||||
override fun randomTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
// todo if a honey soaked leaf block is the first block below this block then over time age that leaf block
|
||||
if (random.nextInt(180) == 0) {
|
||||
|
||||
// todo if leaf block is within 10 blocks of a lead soaked block then age it
|
||||
for (i in 1..10) {
|
||||
|
||||
val abovePos = pos.up(i)
|
||||
val aboveState = world.getBlockState(abovePos)
|
||||
val currentAge = state.get(AGE)
|
||||
|
||||
if (!aboveState.isAir) {
|
||||
if ((aboveState.block is SaccharineLeafBlock && aboveState.get(AGE) > 0)/* ||
|
||||
(aboveState.block is BeehiveBlock && aboveState.get(HONEY_LEVEL) == 5)*/) {
|
||||
// todo -1 age from top block
|
||||
world.setBlockState(abovePos, aboveState.with(AGE, aboveState.get(AGE) - 1), 2)
|
||||
|
||||
// todo +1 age the leaf bottom block
|
||||
world.setBlockState(pos, state.with(AGE, currentAge + 1), 2)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
super.randomTick(state, world, pos, random)
|
||||
}
|
||||
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
return SHAPE
|
||||
}
|
||||
|
||||
// todo make block
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getCollisionShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
if (context is EntityShapeContext && (context.entity as? ItemEntity)?.stack?.isIn(CobblemonItemTags.APRICORNS) == true) {
|
||||
return VoxelShapes.empty()
|
||||
}
|
||||
return super.getCollisionShape(state, world, pos, context)
|
||||
}
|
||||
|
||||
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = false /*state.get(AGE) < MAX_AGE*/
|
||||
|
||||
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = true
|
||||
|
||||
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
|
||||
world.setBlockState(pos, state.with(AGE, state.get(AGE) + 1), 2)
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(AGE, DISTANCE, PERSISTENT, Properties.WATERLOGGED)
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun randomDisplayTick(state: BlockState, world: World, pos: BlockPos, random: Random) {
|
||||
|
||||
if (state.get(AGE) == 2) {
|
||||
for (i in 0 until random.nextInt(1) + 1) {
|
||||
this.spawnHoneyParticles(world, pos, state, .75f)
|
||||
}
|
||||
} else if (state.get(AGE) == 1) {
|
||||
for (i in 0 until random.nextInt(1) + 1) {
|
||||
this.spawnHoneyParticles(world, pos, state, .9f)
|
||||
}
|
||||
}
|
||||
|
||||
// this code was for them aging as time goes on
|
||||
/* if (world.random.nextInt(5) == 0) {
|
||||
|
||||
val currentAge = state.get(AGE)
|
||||
if (currentAge < MAX_AGE) {
|
||||
world.setBlockState(pos, state.with(AGE, currentAge + 1), 2)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
|
||||
/*@Deprecated("Deprecated in Java")
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val blockState = world.getBlockState(pos.down())
|
||||
return blockState.isIn(CobblemonBlockTags.SACCHARINE_LEAVES)
|
||||
}*/
|
||||
|
||||
private fun spawnHoneyParticles(world: World, pos: BlockPos, state: BlockState, rate: Float) {
|
||||
if (state.fluidState.isEmpty && !(world.random.nextFloat() < rate)) {
|
||||
val voxelShape = state.getCollisionShape(world, pos)
|
||||
val d = voxelShape.getMax(Direction.Axis.Y)
|
||||
if (d >= 1.0 && !state.isIn(BlockTags.IMPERMEABLE)) {
|
||||
val e = voxelShape.getMin(Direction.Axis.Y)
|
||||
if (e > 0.0) {
|
||||
this.addHoneyParticle(world, pos, voxelShape, pos.y.toDouble() + e - 0.05)
|
||||
} else {
|
||||
val blockPos = pos.down()
|
||||
val blockState = world.getBlockState(blockPos)
|
||||
val voxelShape2 = blockState.getCollisionShape(world, blockPos)
|
||||
val f = voxelShape2.getMax(Direction.Axis.Y)
|
||||
if ((f < 1.0 || !blockState.isFullCube(world, blockPos)) && blockState.fluidState.isEmpty) {
|
||||
this.addHoneyParticle(world, pos, voxelShape, pos.y.toDouble() - 0.05)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addHoneyParticle(world: World, pos: BlockPos, shape: VoxelShape, height: Double) {
|
||||
this.addHoneyParticle(
|
||||
world, pos.x.toDouble() + shape.getMin(Direction.Axis.X), pos.x.toDouble() + shape.getMax(
|
||||
Direction.Axis.X
|
||||
), pos.z.toDouble() + shape.getMin(Direction.Axis.Z), pos.z.toDouble() + shape.getMax(
|
||||
Direction.Axis.Z
|
||||
), height
|
||||
)
|
||||
}
|
||||
|
||||
private fun addHoneyParticle(world: World, minX: Double, maxX: Double, minZ: Double, maxZ: Double, height: Double) {
|
||||
world.addParticle(
|
||||
ParticleTypes.DRIPPING_HONEY,
|
||||
MathHelper.lerp(world.random.nextDouble(), minX, maxX),
|
||||
height,
|
||||
MathHelper.lerp(world.random.nextDouble(), minZ, maxZ),
|
||||
0.0,
|
||||
0.0,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
|
||||
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
|
||||
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
// todo if item in players hand is glass bottle and AGE is more than 0
|
||||
if (player.getStackInHand(hand).isOf(Items.GLASS_BOTTLE) && state.get(AGE) > 0)
|
||||
{
|
||||
// decrement stack if not in creative mode
|
||||
if (!player.isCreative)
|
||||
player.getStackInHand(hand).decrement(1)
|
||||
|
||||
// give player honey bottle for now
|
||||
player.giveItemStack(Items.HONEY_BOTTLE.defaultStack)
|
||||
|
||||
// todo reset AGE
|
||||
world.setBlockState(pos, state.with(AGE, 0), 2)
|
||||
|
||||
val currentAge = state.get(AGE)
|
||||
} else if (player.getStackInHand(hand).isOf(Items.HONEY_BOTTLE) && state.get(AGE) != 2) {
|
||||
// decrement stack if not in creative mode
|
||||
if (!player.isCreative)
|
||||
player.getStackInHand(hand).decrement(1)
|
||||
|
||||
// todo set age to 2
|
||||
world.setBlockState(pos, state.with(AGE, 2), 2)
|
||||
}
|
||||
|
||||
if (state.get(AGE) != MAX_AGE) {
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
|
||||
//doHarvest(world, state, pos, player)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
/*override fun onBlockBreakStart(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity) {
|
||||
if (state.get(AGE) != MAX_AGE) {
|
||||
return super.onBlockBreakStart(state, world, pos, player)
|
||||
}
|
||||
|
||||
//doHarvest(world, state, pos, player)
|
||||
}*/
|
||||
|
||||
companion object {
|
||||
|
||||
val AGE: IntProperty = Properties.AGE_2
|
||||
val DISTANCE: IntProperty = Properties.DISTANCE_1_7
|
||||
val PERSISTENT: BooleanProperty = Properties.PERSISTENT
|
||||
const val MAX_AGE = Properties.AGE_2_MAX
|
||||
const val MIN_AGE = 0
|
||||
const val DISTANCE_MAX = 7
|
||||
|
||||
private val SHAPE: VoxelShape = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* 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.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.block.grower.ApricornTreeGrower
|
||||
import com.cobblemon.mod.common.block.grower.SaccharineTreeGrower
|
||||
import net.minecraft.block.SaplingBlock
|
||||
class SaccharineSaplingBlock(properties : Settings) : SaplingBlock(SaccharineTreeGrower(), properties)
|
||||
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* 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.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import net.minecraft.block.*
|
||||
import net.minecraft.entity.ItemEntity
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.ai.pathing.NavigationType
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.server.world.ServerWorld
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.EnumProperty
|
||||
import net.minecraft.state.property.IntProperty
|
||||
import net.minecraft.state.property.Properties
|
||||
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.util.math.Direction
|
||||
import net.minecraft.util.math.Direction.Axis
|
||||
import net.minecraft.util.math.random.Random
|
||||
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("DEPRECATION", "OVERRIDE_DEPRECATION")
|
||||
class SaccharineStrippedLogBlock(settings: Settings) : PillarBlock(settings) {
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(AGE, MIN_AGE)
|
||||
.with(AXIS, Axis.Y)
|
||||
}
|
||||
|
||||
override fun onPlaced(world: World?, pos: BlockPos?, state: BlockState?, placer: LivingEntity?, itemStack: ItemStack?) {
|
||||
owner = placer
|
||||
super.onPlaced(world, pos, state, placer, itemStack)
|
||||
}
|
||||
|
||||
override fun hasRandomTicks(state: BlockState) = state.get(AGE) < MAX_AGE
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun randomTick(state: BlockState, world: ServerWorld, pos: BlockPos, random: Random) {
|
||||
|
||||
// todo make it so that it only ages when connected to enough natural-tagged Saccharine Leaves (so, generated or grown, not player placed)
|
||||
|
||||
// this code was for them aging as time goes on
|
||||
if (world.random.nextInt(5) == 0 && owner == null) {
|
||||
|
||||
val currentAge = state.get(AGE)
|
||||
if (currentAge < MAX_AGE) {
|
||||
world.setBlockState(pos, state.with(AGE, currentAge + 1), 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*@Deprecated("Deprecated in Java")
|
||||
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
|
||||
val blockState = world.getBlockState(pos.down())
|
||||
return blockState.isIn(CobblemonBlockTags.SACCHARINE_LEAVES)
|
||||
}*/
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
return SHAPE
|
||||
}
|
||||
|
||||
// todo make block
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun getCollisionShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
|
||||
if (context is EntityShapeContext && (context.entity as? ItemEntity)?.stack?.isIn(CobblemonItemTags.APRICORNS) == true) {
|
||||
return VoxelShapes.empty()
|
||||
}
|
||||
return super.getCollisionShape(state, world, pos, context)
|
||||
}
|
||||
|
||||
/*override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
|
||||
val blockState = defaultState
|
||||
val worldView = ctx.world
|
||||
val blockPos = ctx.blockPos
|
||||
return defaultState.with(PillarBlock.AXIS, ctx.side.axis) as BlockState
|
||||
return if (blockState.canPlaceAt(worldView, blockPos)) blockState else null
|
||||
}*/
|
||||
|
||||
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState? {
|
||||
return if (!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(AGE, AXIS)
|
||||
}
|
||||
|
||||
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
|
||||
|
||||
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
|
||||
// todo if item in players hand is glass bottle and AGE is 1
|
||||
if (player.getStackInHand(hand).isOf(Items.GLASS_BOTTLE) && state.get(AGE) == 1)
|
||||
{
|
||||
// decrement stack if not in creative mode
|
||||
if (!player.isCreative)
|
||||
player.getStackInHand(hand).decrement(1)
|
||||
|
||||
// todo give player Sweet Sap item
|
||||
player.giveItemStack(CobblemonItems.SWEET_SAP.defaultStack)
|
||||
|
||||
// todo reset AGE
|
||||
world.setBlockState(pos, state.with(AGE, 0), 2)
|
||||
|
||||
val currentAge = state.get(AGE)
|
||||
}
|
||||
|
||||
if (state.get(AGE) != MAX_AGE) {
|
||||
return super.onUse(state, world, pos, player, hand, hit)
|
||||
}
|
||||
|
||||
//doHarvest(world, state, pos, player)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
/*override fun onBlockBreakStart(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity) {
|
||||
if (state.get(AGE) != MAX_AGE) {
|
||||
return super.onBlockBreakStart(state, world, pos, player)
|
||||
}
|
||||
|
||||
//doHarvest(world, state, pos, player)
|
||||
}*/
|
||||
|
||||
companion object {
|
||||
|
||||
val AGE: IntProperty = Properties.AGE_1
|
||||
val AXIS: EnumProperty<Axis> = Properties.AXIS
|
||||
const val MAX_AGE = Properties.AGE_1_MAX
|
||||
const val MIN_AGE = 0
|
||||
var owner: LivingEntity? = null
|
||||
|
||||
|
||||
private val SHAPE: VoxelShape = Block.createCuboidShape(0.0, 0.0, 0.0, 16.0, 16.0, 16.0)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* 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 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.ItemPlacementContext
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.state.StateManager
|
||||
import net.minecraft.state.property.BooleanProperty
|
||||
import net.minecraft.state.property.DirectionProperty
|
||||
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.util.math.Direction
|
||||
import net.minecraft.util.math.random.Random
|
||||
import net.minecraft.world.BlockView
|
||||
import net.minecraft.world.World
|
||||
import net.minecraft.world.WorldAccess
|
||||
|
||||
@Suppress("OVERRIDE_DEPRECATION")
|
||||
class SweetIncenseBlock(settings: Settings) : BlockWithEntity(settings), Waterloggable {
|
||||
/*
|
||||
This item, when placed, acts like a candle. It must be lit with a Flint n Steel or Fire Charge, and it can be “put out” by right-clicking on it. Small blue smoke particles will rise from the nub on the top of the block when lit.
|
||||
|
||||
This incense block has special effects on Pokémon spawns, but only when lit. When lit, Sweet Incense increases Pokémon spawn & despawn rates by 1.5x and raises spawn density by 25% in a 16 block radius (32 block diameter) (this number will need to be playtested).
|
||||
|
||||
This essentially means that the player will see a larger number of Pokémon at once, and a higher variety of Pokémon in a given span of time.
|
||||
|
||||
*/
|
||||
|
||||
init {
|
||||
this.defaultState = this.stateManager.defaultState
|
||||
.with(FACING, Direction.NORTH)
|
||||
.with(ITEM_DIRECTION, Direction.NORTH)
|
||||
.with(LIT,false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
val ITEM_DIRECTION = DirectionProperty.of("item_facing")
|
||||
val LIT = BooleanProperty.of("false")
|
||||
}
|
||||
|
||||
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)
|
||||
.with(LIT, false)
|
||||
.with(ITEM_DIRECTION, direction)
|
||||
as BlockState
|
||||
|
||||
if (blockState.canPlaceAt(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
builder.add(ITEM_DIRECTION)
|
||||
builder.add(LIT)
|
||||
}
|
||||
|
||||
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 {
|
||||
// light the incense when flint and steel or fire charge is held
|
||||
if (state.get(LIT) == false && (player.getStackInHand(hand).isOf(Items.FLINT_AND_STEEL.asItem()) ||
|
||||
player.getStackInHand(hand).isOf(Items.FIRE_CHARGE.asItem()))) {
|
||||
world.setBlockState(pos, state.with(LIT, true), 2)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
// unlight the incense when used if it is on
|
||||
if (state.get(LIT) == true && (player.getStackInHand(hand).isOf(Items.FLINT_AND_STEEL.asItem()) == false ||
|
||||
player.getStackInHand(hand).isOf(Items.FIRE_CHARGE.asItem()) == false)) {
|
||||
world.setBlockState(pos, state.with(LIT, false), 2)
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
override fun randomDisplayTick(state: BlockState, world: World, pos: BlockPos, random: Random) {
|
||||
if (state.get(LIT)) {
|
||||
spawnSmokeParticle(world, pos, true, false)
|
||||
|
||||
if (random.nextInt(10) == 0) {
|
||||
world.playSound(
|
||||
pos.x.toDouble() + 0.5,
|
||||
pos.y.toDouble() + 0.5,
|
||||
pos.z.toDouble() + 0.5,
|
||||
SoundEvents.BLOCK_CAMPFIRE_CRACKLE,
|
||||
SoundCategory.BLOCKS,
|
||||
0.2f + random.nextFloat(),
|
||||
random.nextFloat() * 0.7f + 0.6f,
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
/*if (random.nextInt(5) == 0) {
|
||||
for (i in 0 until random.nextInt(1) + 1) {
|
||||
world.addParticle(
|
||||
ParticleTypes.LAVA, pos.x.toDouble() + 0.5, pos.y.toDouble() + 0.5, pos.z.toDouble() + 0.5,
|
||||
(random.nextFloat() / 2.0f).toDouble(), 5.0E-5,
|
||||
(random.nextFloat() / 2.0f).toDouble()
|
||||
)
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
fun spawnSmokeParticle(world: World, pos: BlockPos, isSignal: Boolean, lotsOfSmoke: Boolean) {
|
||||
val random = world.getRandom()
|
||||
val defaultParticleType =
|
||||
if (isSignal) ParticleTypes.CAMPFIRE_SIGNAL_SMOKE else ParticleTypes.CAMPFIRE_COSY_SMOKE
|
||||
world.addImportantParticle(
|
||||
defaultParticleType,
|
||||
true,
|
||||
pos.x.toDouble() + 0.5 + random.nextDouble() / 3.0 * (if (random.nextBoolean()) 1 else -1).toDouble(),
|
||||
pos.y.toDouble() + random.nextDouble() + random.nextDouble(),
|
||||
pos.z.toDouble() + 0.5 + random.nextDouble() / 3.0 * (if (random.nextBoolean()) 1 else -1).toDouble(),
|
||||
0.0,
|
||||
0.07,
|
||||
0.0
|
||||
)
|
||||
if (lotsOfSmoke) {
|
||||
world.addParticle(
|
||||
ParticleTypes.SMOKE,
|
||||
pos.x.toDouble() + 0.5 + random.nextDouble() / 4.0 * (if (random.nextBoolean()) 1 else -1).toDouble(),
|
||||
pos.y.toDouble() + 0.4,
|
||||
pos.z.toDouble() + 0.5 + random.nextDouble() / 4.0 * (if (random.nextBoolean()) 1 else -1).toDouble(),
|
||||
0.0,
|
||||
0.005,
|
||||
0.0
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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 hasComparatorOutput(state: BlockState?) = true
|
||||
|
||||
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
|
||||
|
||||
}
|
||||
@@ -131,7 +131,6 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
|
||||
if (isFake() && (player is ServerPlayerEntity)) {
|
||||
spawnPokemon(world, pos, state, player)
|
||||
}
|
||||
world.setBlockState(pos, if (state.fluidState.isOf(Fluids.WATER)) Blocks.WATER.defaultState else Blocks.AIR.defaultState)
|
||||
val bEntity = world.getBlockEntity(pos) as? GildedChestBlockEntity
|
||||
bEntity?.markRemoved()
|
||||
} else super.onBreak(world, pos, state, player)
|
||||
@@ -187,10 +186,6 @@ class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWit
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
|
||||
import com.cobblemon.mod.common.api.mulch.MulchVariant
|
||||
import com.cobblemon.mod.common.block.BerryBlock
|
||||
import com.cobblemon.mod.common.block.BerryBlock.Companion.getMulch
|
||||
import com.cobblemon.mod.common.block.BerryBlock.Companion.setMulch
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.BlockEntity
|
||||
@@ -43,8 +42,8 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
lateinit var berryIdentifier: Identifier
|
||||
private val ticksPerMinute = 1200
|
||||
var renderState: RenderState? = null
|
||||
//The time left for the tree to grow to stage 5
|
||||
var growthTimer: Int = 72000
|
||||
//The time left for the tree until its either age 3 or age 5 (check block state, if we are at age 0<x<3 unti 3 otherwise until 5)
|
||||
var growthTimer: Int = 0
|
||||
set(value) {
|
||||
if (value < 0) {
|
||||
throw IllegalArgumentException("You cannot set the growth time to less than zero")
|
||||
@@ -55,7 +54,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
field = value
|
||||
}
|
||||
//The time left for the tree to grow to the next stage
|
||||
var stageTimer: Int = growthTimer / BerryBlock.MATURE_AGE
|
||||
var stageTimer: Int = 0
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
this.markDirty()
|
||||
@@ -63,6 +62,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
field = value
|
||||
}
|
||||
private val growthPoints = arrayListOf<Identifier>()
|
||||
var mulchVariant = MulchVariant.NONE
|
||||
|
||||
/**
|
||||
* The idea behind the growth point sequence is it's a 16-long string of
|
||||
@@ -85,20 +85,6 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
markDirty()
|
||||
}
|
||||
|
||||
fun decrementMulchDuration(world: World, pos: BlockPos, state: BlockState) {
|
||||
val currentMulch = getMulch(state)
|
||||
if (currentMulch == MulchVariant.NONE || currentMulch.duration == -1) {
|
||||
return
|
||||
}
|
||||
val newDuration = mulchDuration - 1
|
||||
mulchDuration = if (newDuration <= 0) {
|
||||
setMulch(world, pos, state, MulchVariant.NONE)
|
||||
0
|
||||
} else {
|
||||
newDuration
|
||||
}
|
||||
}
|
||||
|
||||
constructor(pos: BlockPos, state: BlockState, berryIdentifier: Identifier): this(pos, state) {
|
||||
this.berryIdentifier = berryIdentifier
|
||||
resetGrowTimers(pos, state)
|
||||
@@ -107,6 +93,31 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
}
|
||||
}
|
||||
|
||||
fun decrementMulchDuration(world: World, pos: BlockPos, state: BlockState) {
|
||||
if (mulchVariant == MulchVariant.NONE || mulchVariant.duration == -1) {
|
||||
return
|
||||
}
|
||||
val newDuration = mulchDuration - 1
|
||||
mulchDuration = if (newDuration <= 0) {
|
||||
mulchVariant = MulchVariant.NONE
|
||||
this.markDirty()
|
||||
world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS)
|
||||
0
|
||||
} else {
|
||||
newDuration
|
||||
}
|
||||
}
|
||||
|
||||
fun setMulch(mulch: MulchVariant, world: World, state: BlockState, pos: BlockPos) {
|
||||
this.mulchVariant = mulch
|
||||
this.mulchDuration = mulch.duration
|
||||
refreshTimers(pos)
|
||||
world.updateListeners(pos, state, state, Block.NOTIFY_LISTENERS)
|
||||
this.markDirty()
|
||||
}
|
||||
|
||||
//This function basically runs twice when the berry tree is growing, once when it starts, once when it hits age 3
|
||||
//Why dont we just go 0-5? Design thinks its annoying to balance the numbers using 0-5 and 3-5
|
||||
fun resetGrowTimers(pos: BlockPos, state: BlockState) {
|
||||
val curAge = state.get(BerryBlock.AGE)
|
||||
if (curAge == 5) {
|
||||
@@ -119,12 +130,15 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
val upperGrowthLimit = (if (curAge == 0) berry.growthTime.last else berry.refreshRate.last) * multiplier / 10
|
||||
val growthRange = lowerGrowthLimit..upperGrowthLimit
|
||||
|
||||
this.growthTimer = this.applyMulchModifier(pos, growthRange.random() * ticksPerMinute)
|
||||
this.goToNextStageTimer(BerryBlock.FRUIT_AGE - curAge)
|
||||
this.growthTimer = this.applyMulchModifier(pos, growthRange.random() * ticksPerMinute, true)
|
||||
val stagesLeft = if (curAge < 3) BerryBlock.MATURE_AGE - curAge else BerryBlock.FRUIT_AGE - curAge
|
||||
this.goToNextStageTimer(stagesLeft)
|
||||
}
|
||||
|
||||
fun goToNextStageTimer(stagesLeft: Int) {
|
||||
val avgStageTime = growthTimer / stagesLeft
|
||||
//A number between 80% and 100% the average stage time
|
||||
//So stages have some variance
|
||||
stageTimer = this.world?.random?.nextBetween((avgStageTime * 8) / 10, avgStageTime) ?:
|
||||
(((Math.random() * 0.2) + 0.8) * avgStageTime).toInt()
|
||||
growthTimer -= stageTimer
|
||||
@@ -137,7 +151,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
* @param timer Timer to be modified
|
||||
* @return The modified timer
|
||||
*/
|
||||
private fun applyMulchModifier(pos: BlockPos, timer: Int): Int {
|
||||
private fun applyMulchModifier(pos: BlockPos, timer: Int, decrementMulch: Boolean = false): Int {
|
||||
val state = world?.getBlockState(pos) ?: return timer
|
||||
|
||||
val curAge = state.get(BerryBlock.AGE)
|
||||
@@ -145,11 +159,15 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
return timer
|
||||
}
|
||||
|
||||
if (getMulch(state) != MulchVariant.GROWTH) {
|
||||
if (mulchVariant != MulchVariant.GROWTH) {
|
||||
return timer
|
||||
}
|
||||
|
||||
return (timer * 0.66).toInt()
|
||||
if (decrementMulch) {
|
||||
world?.let {
|
||||
decrementMulchDuration(it, pos, state)
|
||||
}
|
||||
}
|
||||
return (timer * MulchVariant.GROWTH_TIME_MULTIPLIER).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -157,8 +175,8 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
* @param pos The position of the block
|
||||
*/
|
||||
fun refreshTimers(pos: BlockPos) {
|
||||
this.growthTimer = this.applyMulchModifier(pos, growthTimer)
|
||||
this.stageTimer = this.applyMulchModifier(pos, stageTimer)
|
||||
this.growthTimer = this.applyMulchModifier(pos, growthTimer, false)
|
||||
this.stageTimer = this.applyMulchModifier(pos, stageTimer, false)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -248,6 +266,9 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
if (nbt.contains(GROWTH_POINTS_SEQUENCE)) {
|
||||
growthPointSequence = nbt.getString(GROWTH_POINTS_SEQUENCE)
|
||||
}
|
||||
if (nbt.contains(MULCH_VARIANT)) {
|
||||
mulchVariant = MulchVariant.valueOf(nbt.getString(MULCH_VARIANT))
|
||||
}
|
||||
this.renderState?.needsRebuild = true
|
||||
}
|
||||
|
||||
@@ -260,6 +281,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
nbt.putString(BERRY, berryIdentifier.toString())
|
||||
nbt.putInt(MULCH_DURATION, mulchDuration)
|
||||
nbt.putString(GROWTH_POINTS_SEQUENCE, growthPointSequence)
|
||||
nbt.putString(MULCH_VARIANT, mulchVariant.toString())
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
@@ -323,11 +345,12 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
companion object {
|
||||
internal val TICKER = BlockEntityTicker<BerryBlockEntity> { world, pos, state, blockEntity ->
|
||||
if (world.isClient) return@BlockEntityTicker
|
||||
if (state.get(BerryBlock.IS_ROOTED)) return@BlockEntityTicker
|
||||
if (blockEntity.stageTimer >= 0) {
|
||||
blockEntity.stageTimer--
|
||||
}
|
||||
if (blockEntity.stageTimer == 0) {
|
||||
(state.block as BerryBlock).grow(world as ServerWorld, world.random, pos, state)
|
||||
(state.block as BerryBlock).growHelper(world as ServerWorld, world.random, pos, state)
|
||||
}
|
||||
}
|
||||
//private const val LIFE_CYCLES = "life_cycles"
|
||||
@@ -337,6 +360,7 @@ class BerryBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblemon
|
||||
private const val STAGE_TIMER = "StageTimer"
|
||||
private const val BERRY = "Berry"
|
||||
private const val MULCH_DURATION = "MulchDuration"
|
||||
private const val MULCH_VARIANT = "MulchVariant"
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+214
@@ -0,0 +1,214 @@
|
||||
package com.cobblemon.mod.common.block.entity
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.block.CookingPotBlock
|
||||
import com.cobblemon.mod.common.gui.CookingPotScreenHandler
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.block.entity.LockableContainerBlockEntity
|
||||
import net.minecraft.block.entity.ViewerCountManager
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.listener.ClientPlayPacketListener
|
||||
import net.minecraft.network.packet.Packet
|
||||
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
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 CookingPotBlockEntity(
|
||||
val blockPos: BlockPos,
|
||||
val blockState: BlockState
|
||||
) : LockableContainerBlockEntity(CobblemonBlockEntities.COOKING_POT, blockPos, blockState) {
|
||||
|
||||
var cookingPotInventory = CookingPotBlockInventory(this)
|
||||
var automationDelay: Int = AUTOMATION_DELAY
|
||||
var partialTicks = 0.0f
|
||||
|
||||
val stateManager: ViewerCountManager = object : ViewerCountManager() {
|
||||
override fun onContainerOpen(world: World, pos: BlockPos, state: BlockState) {
|
||||
this@CookingPotBlockEntity.setOpen(state, true)
|
||||
}
|
||||
|
||||
override fun onContainerClose(world: World, pos: BlockPos, state: BlockState) {
|
||||
this@CookingPotBlockEntity.setOpen(state, false)
|
||||
}
|
||||
|
||||
override fun onViewerCountUpdate(world: World, pos: BlockPos, state: BlockState, oldViewerCount: Int, newViewerCount: Int) {}
|
||||
override fun isPlayerViewing(player: PlayerEntity): Boolean {
|
||||
if (player.currentScreenHandler is CookingPotScreenHandler) {
|
||||
val inventory = (player.currentScreenHandler as CookingPotScreenHandler).inventory
|
||||
return inventory === this@CookingPotBlockEntity
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val AUTOMATION_DELAY = 4
|
||||
const val FILTER_TM_NBT = "FilterTM"
|
||||
}
|
||||
|
||||
override fun getPos(): BlockPos {
|
||||
return blockPos
|
||||
}
|
||||
|
||||
override fun getCachedState(): BlockState {
|
||||
return blockState
|
||||
}
|
||||
|
||||
fun setOpen(state: BlockState, open: Boolean) {
|
||||
world!!.setBlockState(pos, state.with(CookingPotBlock.COOKING, open), Block.NOTIFY_ALL)
|
||||
}
|
||||
|
||||
fun tick() {
|
||||
if (!removed) {
|
||||
stateManager.updateViewerCount(world, pos, cachedState)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
cookingPotInventory.clear()
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return this.cookingPotInventory.size()
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
return this.cookingPotInventory.isEmpty()
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return this.cookingPotInventory.getStack(slot)
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
return this.cookingPotInventory.removeStack(slot, amount)
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
return this.cookingPotInventory.removeStack(slot)
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack?) {
|
||||
return this.cookingPotInventory.setStack(slot, stack)
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?): Boolean {
|
||||
return this.cookingPotInventory.canPlayerUse(player)
|
||||
}
|
||||
|
||||
override fun getContainerName(): Text {
|
||||
return Text.translatable("container.brewing")
|
||||
}
|
||||
|
||||
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?): ScreenHandler {
|
||||
return CookingPotScreenHandler(syncId, playerInventory!!, this.cookingPotInventory, this)
|
||||
}
|
||||
|
||||
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
|
||||
return BlockEntityUpdateS2CPacket.create(this)
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
super.markDirty()
|
||||
if (this.world != null && !this.world!!.isClient) {
|
||||
val currentState = world!!.getBlockState(pos)
|
||||
world!!.setBlockState(pos, currentState, 3) // Flags: 2 | 1 = Block update | Render update
|
||||
this.world!!.updateNeighborsAlways(this.pos, this.cachedState.block)
|
||||
}
|
||||
}
|
||||
|
||||
class CookingPotBlockInventory(val cookingPotBlockEntity: CookingPotBlockEntity) : SidedInventory {
|
||||
private val BLANK_DISC_SLOT_INDEX = 0
|
||||
private val GEM_SLOT_INDEX = 1
|
||||
private val MISC_SLOT_INDEX = 2
|
||||
private val OUTPUT_SLOT_INDEX = 3
|
||||
private val INPUT_SLOTS = intArrayOf(0, 1, 2, 3)
|
||||
val entityPos = this.cookingPotBlockEntity.pos
|
||||
val entityState = this.cookingPotBlockEntity.cachedState
|
||||
val entityWorld = this.cookingPotBlockEntity.world
|
||||
|
||||
var filterTM: ItemStack? = null
|
||||
var items: DefaultedList<ItemStack?>? = DefaultedList.ofSize(4, ItemStack.EMPTY)
|
||||
|
||||
fun hasFilterTM(): Boolean {
|
||||
return filterTM != null
|
||||
}
|
||||
|
||||
override fun clear() {
|
||||
cookingPotBlockEntity.markDirty()
|
||||
this.items?.clear()
|
||||
}
|
||||
|
||||
override fun size(): Int {
|
||||
return this.items?.size ?: 0
|
||||
}
|
||||
|
||||
fun getInvStack(slot: Int): ItemStack {
|
||||
return items?.get(slot) ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun isEmpty(): Boolean {
|
||||
for (i in 0 until size()) {
|
||||
val stack: ItemStack = getInvStack(i)
|
||||
if (!stack.isEmpty) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getStack(slot: Int): ItemStack {
|
||||
return items?.get(slot) ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray? {
|
||||
return if (side == Direction.DOWN) {
|
||||
return listOf(this.OUTPUT_SLOT_INDEX).toIntArray()
|
||||
} else this.INPUT_SLOTS
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int, amount: Int): ItemStack {
|
||||
cookingPotBlockEntity.markDirty()
|
||||
return Inventories.splitStack(items, slot, amount)
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
val slotStack = items?.get(slot)
|
||||
items?.set(slot, ItemStack.EMPTY)
|
||||
cookingPotBlockEntity.markDirty()
|
||||
return slotStack ?: ItemStack.EMPTY
|
||||
}
|
||||
|
||||
override fun setStack(slot: Int, stack: ItemStack?) {
|
||||
items?.set(slot, stack)
|
||||
cookingPotBlockEntity.markDirty()
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
if (cookingPotBlockEntity.world != null)
|
||||
cookingPotBlockEntity.markDirty()
|
||||
}
|
||||
|
||||
override fun canPlayerUse(player: PlayerEntity?): Boolean {
|
||||
return Inventory.canPlayerUse(this.cookingPotBlockEntity, player)
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
return dir == Direction.DOWN && slot == this.OUTPUT_SLOT_INDEX
|
||||
}
|
||||
}
|
||||
}
|
||||
-9
@@ -62,14 +62,6 @@ class FossilAnalyzerBlockEntity(
|
||||
}
|
||||
|
||||
override fun removeStack(slot: Int): ItemStack {
|
||||
// if(analyzerEntity.multiblockStructure is FossilMultiblockStructure) {
|
||||
// val fossilMultiblockStructure = analyzerEntity.multiblockStructure as FossilMultiblockStructure
|
||||
// if (fossilMultiblockStructure.fossilInventory.size > slot) {
|
||||
// val itemStack = fossilMultiblockStructure.fossilInventory.removeAt(slot)
|
||||
// markDirty()
|
||||
// return itemStack
|
||||
// }
|
||||
// }
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
@@ -111,7 +103,6 @@ class FossilAnalyzerBlockEntity(
|
||||
}
|
||||
|
||||
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
// return analyzerEntity.multiblockStructure != null && analyzerEntity.multiblockStructure is FossilMultiblockStructure && dir == Direction.DOWN
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
+2
-6
@@ -38,18 +38,14 @@ open class FossilMultiblockEntity(
|
||||
}
|
||||
}
|
||||
get() {
|
||||
return if (field != null) {
|
||||
field
|
||||
} else if (masterBlockPos != null && masterBlockPos != pos) {
|
||||
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
|
||||
}
|
||||
return field
|
||||
}
|
||||
|
||||
override fun markRemoved() {
|
||||
|
||||
+11
-19
@@ -12,13 +12,11 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
|
||||
import com.cobblemon.mod.common.api.multiblock.builder.MultiblockStructureBuilder
|
||||
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockStructure
|
||||
import net.minecraft.block.Block
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.inventory.Inventories
|
||||
import net.minecraft.inventory.SidedInventory
|
||||
import net.minecraft.inventory.SimpleInventory
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.nbt.NbtCompound
|
||||
import net.minecraft.nbt.NbtList
|
||||
@@ -55,7 +53,9 @@ class RestorationTankBlockEntity(
|
||||
|
||||
constructor(tankEntity: RestorationTankBlockEntity) : this(tankEntity, *Array(8) { ItemStack.EMPTY })
|
||||
|
||||
|
||||
override fun getMaxCountPerStack(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun markDirty() {
|
||||
super.markDirty()
|
||||
@@ -64,7 +64,6 @@ class RestorationTankBlockEntity(
|
||||
if(!itemStack.isEmpty) {
|
||||
val struct = tankEntity.multiblockStructure as? FossilMultiblockStructure
|
||||
val returnIdentifier = NaturalMaterials.getReturnItem(itemStack)
|
||||
|
||||
if(tankEntity.world != null) {
|
||||
if(struct?.insertOrganicMaterial(itemStack, tankEntity.world!!) == true) {
|
||||
removeStack(i)
|
||||
@@ -72,16 +71,15 @@ class RestorationTankBlockEntity(
|
||||
val returnItem = Registries.ITEM.get(returnIdentifier)
|
||||
val returnStack = ItemStack(returnItem, itemStack.count)
|
||||
var done = false
|
||||
if (returnStack.maxCount > 1) { // Only search for a merge if merging could be possible
|
||||
for(j in 0 until size()) {
|
||||
for(j in 1 until size()) {
|
||||
// Look for a inventory slot to merge with
|
||||
val existingStack : ItemStack = getStack(j)
|
||||
if (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount) {
|
||||
if (existingStack.isEmpty
|
||||
|| (Registries.ITEM.getId(existingStack.item) == returnIdentifier && existingStack.count + returnStack.count < existingStack.maxCount)) {
|
||||
setStack(j, ItemStack(returnItem, existingStack.count + returnStack.count))
|
||||
done = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!done) {
|
||||
setStack(i, ItemStack(returnItem, itemStack.count))
|
||||
}
|
||||
@@ -104,7 +102,7 @@ class RestorationTankBlockEntity(
|
||||
}
|
||||
|
||||
override fun getAvailableSlots(side: Direction?): IntArray {
|
||||
return intArrayOf(0,1,2,3,4,5,6,7)
|
||||
return if (side == Direction.DOWN) intArrayOf(0,1,2,3,4,5,6,7) else intArrayOf(0)
|
||||
}
|
||||
|
||||
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
|
||||
@@ -113,16 +111,16 @@ class RestorationTankBlockEntity(
|
||||
val structure = tankEntity.multiblockStructure as FossilMultiblockStructure
|
||||
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
|
||||
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
|
||||
&& structure.createdPokemon == null
|
||||
&& !structure.hasCreatedPokemon
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
|
||||
val returnStack = ItemStack(Registries.ITEM.get(returnItem), stack.count)
|
||||
if(canUtilize && super.canInsert(returnStack)) {
|
||||
// See if there's room for the return
|
||||
if(returnStack == ItemStack.EMPTY) {
|
||||
if(returnStack == ItemStack.EMPTY && getStack(0) == ItemStack.EMPTY) {
|
||||
return true
|
||||
}
|
||||
// See if there's room for the return item
|
||||
var emptyCount = 0
|
||||
for(i in 0 until size()) {
|
||||
for(i in 1 until size()) {
|
||||
val existingStack : ItemStack = getStack(i)
|
||||
if(existingStack == ItemStack.EMPTY) {
|
||||
emptyCount += 1
|
||||
@@ -130,14 +128,8 @@ class RestorationTankBlockEntity(
|
||||
// 2 empty slots, safe to insert
|
||||
return true
|
||||
}
|
||||
} else if(returnStack.maxCount > 1) {
|
||||
if(Registries.ITEM.getId(existingStack.item) == returnItem && existingStack.count + returnStack.count < existingStack.maxCount) {
|
||||
// Return item can merge with an item already in the inventory
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.grower
|
||||
|
||||
import com.cobblemon.mod.common.api.apricorn.Apricorn
|
||||
import com.cobblemon.mod.common.world.feature.CobblemonConfiguredFeatures
|
||||
import net.minecraft.block.sapling.SaplingGenerator
|
||||
import net.minecraft.util.math.random.Random
|
||||
|
||||
class SaccharineTreeGrower() : SaplingGenerator() {
|
||||
override fun getTreeFeature(random: Random, bl: Boolean) = CobblemonConfiguredFeatures.SACCHARINE_TREE_KEY
|
||||
}
|
||||
+63
-54
@@ -10,7 +10,8 @@ package com.cobblemon.mod.common.block.multiblock
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.advancement.CobblemonCriteria
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents
|
||||
import com.cobblemon.mod.common.api.events.pokemon.FossilRevivedEvent
|
||||
import com.cobblemon.mod.common.api.fossil.Fossil
|
||||
import com.cobblemon.mod.common.api.fossil.Fossils
|
||||
import com.cobblemon.mod.common.api.fossil.NaturalMaterials
|
||||
@@ -72,7 +73,7 @@ class FossilMultiblockStructure (
|
||||
var organicMaterialInside = 0
|
||||
private set
|
||||
|
||||
var createdPokemon: Pokemon? = null
|
||||
var hasCreatedPokemon: Boolean = false
|
||||
private set
|
||||
var timeRemaining = -1
|
||||
private set
|
||||
@@ -102,8 +103,7 @@ class FossilMultiblockStructure (
|
||||
if (player !is ServerPlayerEntity) {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
if (this.createdPokemon != null) {
|
||||
|
||||
if (this.hasCreatedPokemon) {
|
||||
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 {
|
||||
@@ -114,25 +114,28 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
|
||||
|
||||
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!!)
|
||||
val pokemon = this.resultingFossil?.result?.create()
|
||||
|
||||
if(pokemon != null) {
|
||||
pokemon.caughtBall = ballType
|
||||
player.party().add(pokemon)
|
||||
this.fossilState.growthState = "Taken"
|
||||
player.playSound(CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS, 1.0F, 1.0F)
|
||||
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, player))
|
||||
}
|
||||
|
||||
// Turn the monitor off
|
||||
val monitorState = world.getBlockState(monitorPos)
|
||||
if(!monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
|
||||
if(monitorState.contains(MonitorBlock.SCREEN) && !monitorState.equals(MonitorBlock.MonitorScreen.OFF)) {
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, MonitorBlock.MonitorScreen.OFF))
|
||||
}
|
||||
|
||||
this.createdPokemon = null
|
||||
this.hasCreatedPokemon = false
|
||||
this.fossilOwnerUUID = null
|
||||
this.protectionTime = -1
|
||||
this.updateFossilType(world)
|
||||
@@ -144,15 +147,14 @@ class FossilMultiblockStructure (
|
||||
|
||||
// Reclaim the last fossil from the machine if their hand is empty
|
||||
if (player.getStackInHand(interactionHand).isEmpty) {
|
||||
if(!this.isRunning() && this.createdPokemon == null) {
|
||||
if(!this.isRunning() && !this.hasCreatedPokemon) {
|
||||
if (fossilInventory.isEmpty()) {
|
||||
return ActionResult.CONSUME
|
||||
}
|
||||
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) {
|
||||
if(player is ServerPlayerEntity) {
|
||||
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)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.updateFossilType(world)
|
||||
this.syncToClient(world)
|
||||
@@ -164,19 +166,17 @@ class FossilMultiblockStructure (
|
||||
|
||||
// Check if the player is holding a fossil and if so insert it into the machine.
|
||||
if (Fossils.isFossilIngredient(stack)) {
|
||||
if(!this.isRunning() && this.createdPokemon == null) {
|
||||
if (!this.isRunning() && !this.hasCreatedPokemon) {
|
||||
if (fossilInventory.size > Cobblemon.config.maxInsertedFossilItems) {
|
||||
return ActionResult.FAIL
|
||||
}
|
||||
|
||||
val copyFossilStack = stack.copyWithCount(1)
|
||||
if (!player.isCreative) {
|
||||
stack?.decrement(1)
|
||||
}
|
||||
|
||||
fossilOwnerUUID = player.uuid
|
||||
fossilInventory.add(copyFossilStack)
|
||||
if(!world.isClient) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
val copyFossilStack = stack.copyWithCount(1)
|
||||
if (!player.isCreative) {
|
||||
stack?.decrement(1)
|
||||
}
|
||||
fossilOwnerUUID = player.uuid
|
||||
fossilInventory.add(copyFossilStack)
|
||||
this.updateFossilType(world)
|
||||
world.playSound(null, analyzerPos, CobblemonSounds.FOSSIL_MACHINE_INSERT_FOSSIL, SoundCategory.BLOCKS)
|
||||
this.syncToClient(world)
|
||||
@@ -188,7 +188,11 @@ class FossilMultiblockStructure (
|
||||
|
||||
// Check if the player is holding a natural material and if so, feed it to the machine.
|
||||
if (NaturalMaterials.isNaturalMaterial(stack)) {
|
||||
if (!this.isRunning() && this.createdPokemon == null && this.organicMaterialInside < MATERIAL_TO_START && insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
|
||||
if (player is ServerPlayerEntity
|
||||
&& !this.isRunning()
|
||||
&& !this.hasCreatedPokemon
|
||||
&& this.organicMaterialInside < MATERIAL_TO_START
|
||||
&& insertOrganicMaterial(ItemStack(stack.item, 1), world)) {
|
||||
this.lastInteraction = world.time
|
||||
if (!player.isCreative) {
|
||||
val returnItem = NaturalMaterials.getReturnItem(stack)
|
||||
@@ -220,6 +224,7 @@ class FossilMultiblockStructure (
|
||||
entity.setPosition(fixedPosition.toCenterPos().subtract(0.0, 0.5, 0.0))
|
||||
// TODO: Find a correct way to set the new entity's Yaw rotation. (Face away from the machine)
|
||||
if (world.spawnEntity(entity)) {
|
||||
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, null))
|
||||
return true
|
||||
} else {
|
||||
Cobblemon.LOGGER.warn("Couldn't spawn resurrected Pokémon for some reason")
|
||||
@@ -271,7 +276,7 @@ class FossilMultiblockStructure (
|
||||
return 0
|
||||
}
|
||||
if(monitorPos == pos) {
|
||||
if(createdPokemon != null) {
|
||||
if(hasCreatedPokemon) {
|
||||
return 15
|
||||
}
|
||||
if(!isRunning()) {
|
||||
@@ -287,13 +292,13 @@ class FossilMultiblockStructure (
|
||||
override fun onTriggerEvent(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: Random?) {
|
||||
// instantiate the pokemon as a new entity and spawn it at the location of the machine
|
||||
if(this.protectionTime <= 0) {
|
||||
val wildPokemon: Pokemon = this.createdPokemon ?: return
|
||||
val wildPokemon: Pokemon = if (hasCreatedPokemon) resultingFossil?.result?.create() ?: return else return
|
||||
val direction = state?.get(HorizontalFacingBlock.FACING)?.opposite
|
||||
if(pos != null && direction != null && world != null) {
|
||||
val success = this.spawn(world, pos, direction, wildPokemon)
|
||||
if(success) {
|
||||
this.fossilState.growthState = "Taken"
|
||||
this.createdPokemon = null
|
||||
this.hasCreatedPokemon = false
|
||||
this.fossilOwnerUUID = null
|
||||
this.protectionTime = -1
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_RETRIEVE_POKEMON, SoundCategory.BLOCKS)
|
||||
@@ -310,9 +315,9 @@ class FossilMultiblockStructure (
|
||||
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
|
||||
val tankBaseBlockState = world.getBlockState(tankBaseEntity?.pos)
|
||||
val direction = tankBaseBlockState.get(HorizontalFacingBlock.FACING).opposite
|
||||
val wildPokemon: Pokemon? = if(hasCreatedPokemon) resultingFossil?.result?.create() else null
|
||||
|
||||
monitorEntity?.multiblockStructure = null
|
||||
analyzerEntity?.multiblockStructure = null
|
||||
@@ -372,7 +377,7 @@ class FossilMultiblockStructure (
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_UNPROTECTED, SoundCategory.BLOCKS)
|
||||
}
|
||||
|
||||
if (this.createdPokemon != null) {
|
||||
if (this.hasCreatedPokemon) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -401,10 +406,7 @@ class FossilMultiblockStructure (
|
||||
if (this.timeRemaining == 0) {
|
||||
world.playSound(null, tankBasePos, CobblemonSounds.FOSSIL_MACHINE_FINISHED, SoundCategory.BLOCKS)
|
||||
fossilInventory.clear()
|
||||
|
||||
this.resultingFossil?.let {
|
||||
this.createdPokemon = it.result.create()
|
||||
}
|
||||
this.hasCreatedPokemon = true
|
||||
if(this.fossilOwnerUUID != null) {
|
||||
protectionTime = PROTECTION_TIME
|
||||
}
|
||||
@@ -475,20 +477,26 @@ class FossilMultiblockStructure (
|
||||
val upperTankPos = tankBasePos.up()
|
||||
val analyzerState = world.getBlockState(analyzerPos)
|
||||
val tankState = world.getBlockState(tankBasePos.up())
|
||||
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
|
||||
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
|
||||
if (analyzerState.contains(FossilAnalyzerBlock.ON)) {
|
||||
world.setBlockState(analyzerPos, analyzerState.with(FossilAnalyzerBlock.ON, timeRemaining >= 0))
|
||||
}
|
||||
if (tankState.contains(RestorationTankBlock.ON)) {
|
||||
world.setBlockState(upperTankPos, tankState.with(RestorationTankBlock.ON, timeRemaining >= 0))
|
||||
}
|
||||
}
|
||||
|
||||
fun updateProgress(world: World) {
|
||||
val screenID = if(protectionTime > 0F ) {
|
||||
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
|
||||
} else if (timeRemaining <= 0) {
|
||||
MonitorBlock.MonitorScreen.OFF
|
||||
} else {
|
||||
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
|
||||
}
|
||||
val monitorState = world.getBlockState(monitorPos)
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
|
||||
if (monitorState.contains(MonitorBlock.SCREEN)) {
|
||||
val screenID = if (protectionTime > 0F) {
|
||||
MonitorBlock.MonitorScreen.GREEN_PROGRESS_9
|
||||
} else if (timeRemaining <= 0) {
|
||||
MonitorBlock.MonitorScreen.OFF
|
||||
} else {
|
||||
getProgressScreen((TIME_TO_TAKE - timeRemaining) / TIME_PER_STAGE)
|
||||
}
|
||||
world.setBlockState(monitorPos, monitorState.with(MonitorBlock.SCREEN, screenID))
|
||||
}
|
||||
}
|
||||
|
||||
fun getProgressScreen(progress:Int) : MonitorBlock.MonitorScreen {
|
||||
@@ -603,9 +611,7 @@ class FossilMultiblockStructure (
|
||||
result.putString(DataKeys.INSERTED_FOSSIL, this.resultingFossil!!.asString())
|
||||
}
|
||||
|
||||
if (this.createdPokemon != null) {
|
||||
result.put(DataKeys.CREATED_POKEMON, createdPokemon!!.saveToNBT(NbtCompound()))
|
||||
}
|
||||
result.putBoolean(DataKeys.HAS_CREATED_POKEMON, hasCreatedPokemon)
|
||||
|
||||
return result
|
||||
}
|
||||
@@ -654,7 +660,10 @@ class FossilMultiblockStructure (
|
||||
}
|
||||
|
||||
if (nbt.contains(DataKeys.CREATED_POKEMON)) {
|
||||
result.createdPokemon = Pokemon.loadFromNBT(nbt.getCompound(DataKeys.CREATED_POKEMON))
|
||||
// migration of instances that saved the created pokeon in the nbt
|
||||
result.hasCreatedPokemon = true
|
||||
} else if (nbt.contains(DataKeys.HAS_CREATED_POKEMON)){
|
||||
result.hasCreatedPokemon = nbt.getBoolean(DataKeys.HAS_CREATED_POKEMON)
|
||||
}
|
||||
result.fillLevel = result.organicMaterialInside * 8 / MATERIAL_TO_START
|
||||
return result
|
||||
|
||||
@@ -62,6 +62,46 @@ object CobblemonBakingOverrides {
|
||||
cobblemonModel("restoration_tank_connector", "none")
|
||||
)
|
||||
|
||||
val COARSE_MULCH = registerOverride(
|
||||
cobblemonResource("block/coarse_mulch"),
|
||||
cobblemonModel("coarse_mulch", "none")
|
||||
)
|
||||
|
||||
val GROWTH_MULCH = registerOverride(
|
||||
cobblemonResource("block/growth_mulch"),
|
||||
cobblemonModel("growth_mulch", "none")
|
||||
)
|
||||
|
||||
val HUMID_MULCH = registerOverride(
|
||||
cobblemonResource("block/humid_mulch"),
|
||||
cobblemonModel("humid_mulch", "none")
|
||||
)
|
||||
|
||||
val LOAMY_MULCH = registerOverride(
|
||||
cobblemonResource("block/loamy_mulch"),
|
||||
cobblemonModel("loamy_mulch", "none")
|
||||
)
|
||||
|
||||
val PEAT_MULCH = registerOverride(
|
||||
cobblemonResource("block/peat_mulch"),
|
||||
cobblemonModel("peat_mulch", "none")
|
||||
)
|
||||
|
||||
val RICH_MULCH = registerOverride(
|
||||
cobblemonResource("block/rich_mulch"),
|
||||
cobblemonModel("rich_mulch", "none")
|
||||
)
|
||||
|
||||
val SANDY_MULCH = registerOverride(
|
||||
cobblemonResource("block/sandy_mulch"),
|
||||
cobblemonModel("sandy_mulch", "none")
|
||||
)
|
||||
|
||||
val SURPRISE_MULCH = registerOverride(
|
||||
cobblemonResource("block/surprise_mulch"),
|
||||
cobblemonModel("surprise_mulch", "none")
|
||||
)
|
||||
|
||||
fun registerOverride(modelLocation: Identifier, modelIdentifier: ModelIdentifier): BakingOverride {
|
||||
val result = BakingOverride(modelLocation, modelIdentifier)
|
||||
models.add(result)
|
||||
|
||||
@@ -50,10 +50,14 @@ 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.gui.CobblemonScreenHandlers
|
||||
import com.cobblemon.mod.common.gui.CobblemonScreenHandlers.COOKING_POT_SCREEN
|
||||
import com.cobblemon.mod.common.gui.cooking.CookingPotHandledScreen
|
||||
import com.cobblemon.mod.common.platform.events.PlatformEvents
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreens
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.block.entity.HangingSignBlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.SignBlockEntityRenderer
|
||||
@@ -110,6 +114,7 @@ object CobblemonClient {
|
||||
//registerColors()
|
||||
registerFlywheelRenderers()
|
||||
this.registerEntityRenderers()
|
||||
registerHandledScreens()
|
||||
Berries.observable.subscribe {
|
||||
BerryModelRepository.patchModels()
|
||||
}
|
||||
@@ -221,7 +226,16 @@ object CobblemonClient {
|
||||
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
|
||||
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
|
||||
CobblemonBlocks.GIMMIGHOUL_CHEST,
|
||||
CobblemonBlocks.DISPLAY_CASE
|
||||
CobblemonBlocks.DISPLAY_CASE,
|
||||
CobblemonBlocks.COOKING_POT,
|
||||
CobblemonBlocks.SACCHARINE_DOOR,
|
||||
CobblemonBlocks.SACCHARINE_TRAPDOOR,
|
||||
CobblemonBlocks.SACCHARINE_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_WALL_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_HANGING_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_WALL_HANGING_SIGN,
|
||||
CobblemonBlocks.SACCHARINE_SAPLING,
|
||||
CobblemonBlocks.BUGWORT
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -253,6 +267,7 @@ object CobblemonClient {
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
|
||||
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.COOKING_POT, ::CookingPotRenderer)
|
||||
}
|
||||
|
||||
private fun registerEntityRenderers() {
|
||||
@@ -306,4 +321,11 @@ object CobblemonClient {
|
||||
}
|
||||
}
|
||||
|
||||
private fun registerHandledScreens() {
|
||||
HandledScreens.register(
|
||||
CobblemonScreenHandlers.COOKING_POT_SCREEN,
|
||||
::CookingPotHandledScreen
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-3
@@ -15,9 +15,6 @@ import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.math.RotationAxis.NEGATIVE_X
|
||||
import net.minecraft.util.math.RotationAxis.POSITIVE_X
|
||||
import net.minecraft.util.math.RotationAxis.POSITIVE_Y
|
||||
|
||||
object SpawnSnowstormParticleHandler : ClientNetworkPacketHandler<SpawnSnowstormParticlePacket> {
|
||||
override fun handle(packet: SpawnSnowstormParticlePacket, client: MinecraftClient) {
|
||||
|
||||
@@ -10,16 +10,20 @@ package com.cobblemon.mod.common.client.particle
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunction
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies.species
|
||||
import com.cobblemon.mod.common.api.snowstorm.BedrockParticleEffect
|
||||
import com.cobblemon.mod.common.api.snowstorm.ParticleEmitterAction
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.SnowstormParticle
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.particle.SnowstormParticleEffect
|
||||
import com.cobblemon.mod.common.util.math.geometry.transformDirection
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.particle.NoRenderParticle
|
||||
import net.minecraft.client.util.ParticleUtil.spawnParticle
|
||||
import net.minecraft.client.world.ClientWorld
|
||||
import net.minecraft.entity.Entity
|
||||
import net.minecraft.util.math.Vec3d
|
||||
@@ -48,12 +52,22 @@ class ParticleStorm(
|
||||
.addFunction("entity_height") { DoubleValue(entity.boundingBox.yLength) }
|
||||
.addFunction("entity_size") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength }) }
|
||||
.addFunction("entity_radius") { DoubleValue(entity.boundingBox.run { if (xLength > yLength) xLength else yLength } / 2) }
|
||||
.addFunction("entity_scale") {
|
||||
val pokeEntity = entity as? PokemonEntity
|
||||
val pokemon = pokeEntity?.pokemon
|
||||
//Use form data if available, species as fall back
|
||||
val baseScale = pokemon?.form?.baseScale ?: pokemon?.species?.baseScale ?: 1.0F
|
||||
val pokemonScale = pokemon?.scaleModifier ?: 1.0F
|
||||
val entityScale = pokeEntity?.scaleFactor ?: 1.0F
|
||||
DoubleValue(baseScale * pokemonScale * entityScale)
|
||||
}
|
||||
// TODO replace with a generified call to if (entity is MoLangEntity) entity.applyVariables(env) or w/e
|
||||
runtime.environment.setSimpleVariable("entity_width", DoubleValue(entity.boundingBox.xLength))
|
||||
runtime.environment.setSimpleVariable("entity_height", DoubleValue(entity.boundingBox.yLength))
|
||||
val longerDiameter = entity.boundingBox.run { if (xLength > yLength) xLength else yLength }
|
||||
runtime.environment.setSimpleVariable("entity_size", DoubleValue(longerDiameter))
|
||||
runtime.environment.setSimpleVariable("entity_radius", DoubleValue(longerDiameter / 2))
|
||||
runtime.environment.setSimpleVariable("entity_scale", DoubleValue((entity as? PokemonEntity)?.scaleFactor ?: 1.0))
|
||||
}
|
||||
MinecraftClient.getInstance().particleManager.addParticle(this)
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class SnowstormParticle(
|
||||
|
||||
init {
|
||||
setVelocity(initialVelocity.x, initialVelocity.y, initialVelocity.z)
|
||||
angle = storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
|
||||
angle = -storm.effect.particle.rotation.getInitialRotation(storm.runtime).toFloat()
|
||||
prevAngle = angle
|
||||
angularVelocity = storm.effect.particle.rotation.getInitialAngularVelocity(storm.runtime)
|
||||
velocityMultiplier = 1F
|
||||
@@ -256,24 +256,20 @@ class SnowstormParticle(
|
||||
setParticleAgeInRuntime()
|
||||
storm.effect.curves.forEach { it.apply(storm.runtime) }
|
||||
storm.runtime.execute(storm.effect.particle.updateExpressions)
|
||||
angularVelocity += storm.effect.particle.rotation.getAngularAcceleration(storm.runtime, angularVelocity) / 20
|
||||
angularVelocity = storm.effect.particle.rotation.getAngularVelocity(storm.runtime, -angle.toDouble(), angularVelocity) / 20
|
||||
|
||||
if (age > maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
|
||||
if (age >= maxAge || storm.runtime.resolveBoolean(storm.effect.particle.killExpression)) {
|
||||
runExpirationEvents()
|
||||
markDead()
|
||||
return
|
||||
} else {
|
||||
val acceleration = storm.effect.particle.motion.getAcceleration(
|
||||
storm.runtime,
|
||||
Vec3d(velocityX, velocityY, velocityZ).multiply(20.0) // Uses blocks per second, not blocks per tick
|
||||
).multiply(1 / 20.0).multiply(1 / 20.0) // blocks per second per second -> blocks per tick per tick
|
||||
|
||||
velocityX += acceleration.x
|
||||
velocityY += acceleration.y
|
||||
velocityZ += acceleration.z
|
||||
|
||||
val velocity = storm.effect.particle.motion.getVelocity(storm.runtime, this, Vec3d(velocityX, velocityY, velocityZ))
|
||||
velocityX = velocity.x
|
||||
velocityY = velocity.y
|
||||
velocityZ = velocity.z
|
||||
prevAngle = angle
|
||||
angle = prevAngle + angularVelocity.toFloat()
|
||||
// Subtract because Bedrock particles are counter-clockwise and Java Edition is clockwise.
|
||||
angle = prevAngle - angularVelocity.toFloat()
|
||||
}
|
||||
|
||||
viewDirection = storm.effect.particle.viewDirection.getDirection(
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ class BerryBlockEntityRenderState : BerryBlockEntity.RenderState {
|
||||
override var needsRebuild = true
|
||||
val vbo: VertexBuffer = VertexBuffer(VertexBuffer.Usage.STATIC)
|
||||
var vboLightLevel: Int = 0
|
||||
var drawVbo: Boolean = false
|
||||
|
||||
override fun close() {
|
||||
vbo.close()
|
||||
|
||||
+72
-15
@@ -23,6 +23,7 @@ import net.minecraft.client.gl.ShaderProgram
|
||||
import net.minecraft.client.gl.VertexBuffer
|
||||
import net.minecraft.client.render.GameRenderer
|
||||
import net.minecraft.client.render.LightmapTextureManager
|
||||
import net.minecraft.client.render.RenderLayer
|
||||
import net.minecraft.client.render.Tessellator
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.VertexFormat
|
||||
@@ -33,6 +34,7 @@ import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.util.math.Box
|
||||
import net.minecraft.util.math.Vec3d
|
||||
import org.joml.Matrix4f
|
||||
import org.joml.Vector3f
|
||||
|
||||
|
||||
class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context) : BlockEntityRenderer<BerryBlockEntity> {
|
||||
@@ -45,33 +47,88 @@ class BerryBlockRenderer(private val context: BlockEntityRendererFactory.Context
|
||||
override fun render(entity: BerryBlockEntity, tickDelta: Float, matrices: MatrixStack, vertexConsumers: VertexConsumerProvider, light: Int, overlay: Int) {
|
||||
if (!isInRenderDistance(entity, entity.pos.toVec3d())) return
|
||||
val blockState = entity.cachedState
|
||||
val age = blockState.get(BerryBlock.AGE)
|
||||
if (age <= BerryBlock.MATURE_AGE) {
|
||||
return
|
||||
}
|
||||
if (entity.renderState == null) {
|
||||
entity.renderState = BerryBlockEntityRenderState()
|
||||
}
|
||||
val renderState = entity.renderState as BerryBlockEntityRenderState
|
||||
if (renderState.needsRebuild || renderState.vboLightLevel != light) {
|
||||
renderToBuffer(entity, light, overlay, renderState.vbo)
|
||||
renderToBuffer(entity, light, overlay, renderState)
|
||||
renderState.vboLightLevel = light
|
||||
(entity.renderState as BerryBlockEntityRenderState).needsRebuild = false
|
||||
}
|
||||
if (renderState.drawVbo) {
|
||||
matrices.push()
|
||||
CobblemonRenderLayers.BERRY_LAYER.startDrawing()
|
||||
renderState.vbo.bind()
|
||||
renderState.vbo.draw(
|
||||
matrices.peek().positionMatrix.mul(RenderSystem.getModelViewMatrix()),
|
||||
RenderSystem.getProjectionMatrix(),
|
||||
GameRenderer.getRenderTypeCutoutProgram()
|
||||
)
|
||||
VertexBuffer.unbind()
|
||||
CobblemonRenderLayers.BERRY_LAYER.endDrawing()
|
||||
matrices.pop()
|
||||
}
|
||||
drawMulch(matrices, vertexConsumers, entity, light, overlay)
|
||||
}
|
||||
|
||||
private fun drawMulch(
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
entity: BerryBlockEntity,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
matrices.push()
|
||||
CobblemonRenderLayers.BERRY_LAYER.startDrawing()
|
||||
renderState.vbo.bind()
|
||||
renderState.vbo.draw(
|
||||
matrices.peek().positionMatrix.mul(RenderSystem.getModelViewMatrix()),
|
||||
RenderSystem.getProjectionMatrix(),
|
||||
GameRenderer.getRenderTypeCutoutProgram()
|
||||
)
|
||||
VertexBuffer.unbind()
|
||||
CobblemonRenderLayers.BERRY_LAYER.endDrawing()
|
||||
//Mulch is rendered on a different layer than the actual berries so
|
||||
val mulchBuf = vertexConsumers.getBuffer(RenderLayer.getCutout())
|
||||
val model = entity.mulchVariant.model
|
||||
model?.let {
|
||||
it.getModel().getQuads(entity.cachedState, null, entity.world?.random).forEach { quad ->
|
||||
mulchBuf.quad(matrices.peek(), quad, 1F, 1F, 1F, light, overlay)
|
||||
}
|
||||
}
|
||||
matrices.pop()
|
||||
}
|
||||
|
||||
fun renderToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
|
||||
|
||||
fun renderToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, renderState: BerryBlockEntityRenderState) {
|
||||
if (entity.cachedState.get(BerryBlock.AGE) == 0) {
|
||||
renderBabyToBuffer(entity, light, overlay, renderState.vbo)
|
||||
renderState.drawVbo = true
|
||||
}
|
||||
else if (entity.cachedState.get(BerryBlock.AGE) > BerryBlock.MATURE_AGE){
|
||||
renderAdultToBuffer(entity, light, overlay, renderState.vbo)
|
||||
renderState.drawVbo = true
|
||||
}
|
||||
else {
|
||||
renderState.drawVbo = false
|
||||
}
|
||||
}
|
||||
|
||||
fun renderBabyToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
|
||||
val bufferBuilder = Tessellator.getInstance().buffer
|
||||
bufferBuilder.begin(CobblemonRenderLayers.BERRY_LAYER.drawMode, CobblemonRenderLayers.BERRY_LAYER.vertexFormat)
|
||||
val berry = entity.berry() ?: return
|
||||
val model = BerryModelRepository.modelOf(berry.fruitModelIdentifier) ?: return
|
||||
val pos = berry.stageOnePositioning.position
|
||||
model.setPosition(Axis.X_AXIS.ordinal, pos.x.toFloat())
|
||||
model.setPosition(Axis.Y_AXIS.ordinal, pos.y.toFloat())
|
||||
model.setPosition(Axis.Z_AXIS.ordinal, pos.z.toFloat())
|
||||
val rot = berry.stageOnePositioning.rotation
|
||||
model.setAngles(
|
||||
Math.toRadians(180 - rot.x).toFloat(),
|
||||
Math.toRadians(180 + rot.y).toFloat(),
|
||||
Math.toRadians(rot.z).toFloat()
|
||||
)
|
||||
model.render(MatrixStack(), bufferBuilder, light, overlay)
|
||||
val bufferBuilderFinal = bufferBuilder.end()
|
||||
buffer.bind()
|
||||
buffer.upload(bufferBuilderFinal)
|
||||
VertexBuffer.unbind()
|
||||
}
|
||||
|
||||
fun renderAdultToBuffer(entity: BerryBlockEntity, light: Int, overlay: Int, buffer: VertexBuffer) {
|
||||
val blockState = entity.cachedState
|
||||
val age = blockState.get(BerryBlock.AGE)
|
||||
if (age <= BerryBlock.MATURE_AGE) {
|
||||
|
||||
+166
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* 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.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.block.DisplayCaseBlock
|
||||
import com.cobblemon.mod.common.block.entity.CookingPotBlockEntity
|
||||
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.item.PokemonItem
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.render.OverlayTexture
|
||||
import net.minecraft.client.render.VertexConsumer
|
||||
import net.minecraft.client.render.VertexConsumerProvider
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRenderer
|
||||
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory
|
||||
import net.minecraft.client.render.model.json.ModelTransformationMode
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.item.BannerItem
|
||||
import net.minecraft.item.BedItem
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.nbt.NbtInt
|
||||
import net.minecraft.util.math.Direction
|
||||
import net.minecraft.util.math.RotationAxis
|
||||
import net.minecraft.world.World
|
||||
|
||||
class CookingPotRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEntityRenderer<CookingPotBlockEntity> {
|
||||
//val coinPouchStack: ItemStack by lazy { ItemStack(CobblemonItems.RELIC_COIN_POUCH).also { it.setSubNbt("CustomModelData", NbtInt.of(1)) } }
|
||||
override fun render(
|
||||
entity: CookingPotBlockEntity,
|
||||
tickDelta: Float,
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
light: Int,
|
||||
overlay: Int
|
||||
) {
|
||||
/*val stack: ItemStack = if (entity.getStack().isOf(CobblemonItems.RELIC_COIN_POUCH)) {
|
||||
coinPouchStack
|
||||
} else {
|
||||
entity.getStack()
|
||||
}
|
||||
val world = entity.world ?: return
|
||||
val posType = getPositioningType(stack, world)
|
||||
val blockState = if (entity.world != null) entity.cachedState
|
||||
else (CobblemonBlocks.DISPLAY_CASE.defaultState.with(DisplayCaseBlock.ITEM_DIRECTION, Direction.NORTH))
|
||||
val yRot = if (posType == PositioningType.ITEM_MODEL) blockState.get(DisplayCaseBlock.ITEM_DIRECTION).opposite.asRotation()
|
||||
else blockState.get(DisplayCaseBlock.ITEM_DIRECTION).asRotation()
|
||||
|
||||
if (stack.item is PokemonItem) {
|
||||
renderPokemon(
|
||||
matrices,
|
||||
vertexConsumers,
|
||||
light,
|
||||
stack,
|
||||
yRot
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
matrices.push()
|
||||
matrices.translate(0.5f, 0.4f, 0.5f)
|
||||
|
||||
matrices.scale(posType.scaleX, posType.scaleY, posType.scaleZ)
|
||||
matrices.translate(posType.transX, posType.transY, posType.transZ)
|
||||
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(-yRot))
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(posType.rotY))
|
||||
|
||||
MinecraftClient.getInstance().itemRenderer.renderItem(
|
||||
stack,
|
||||
ModelTransformationMode.GROUND,
|
||||
light,
|
||||
overlay,
|
||||
matrices,
|
||||
vertexConsumers,
|
||||
entity.world,
|
||||
0
|
||||
)
|
||||
|
||||
matrices.pop()*/
|
||||
|
||||
}
|
||||
|
||||
private fun renderPokemon(
|
||||
matrices: MatrixStack,
|
||||
vertexConsumers: VertexConsumerProvider,
|
||||
light: Int,
|
||||
stack: ItemStack,
|
||||
yRot: Float
|
||||
) {
|
||||
val item = stack.item as? PokemonItem ?: return
|
||||
val pokemon = item.asPokemon(stack) ?: return
|
||||
val model = PokemonModelRepository.getPoser(pokemon.species.resourceIdentifier, pokemon.aspects)
|
||||
val renderLayer = model.getLayer(PokemonModelRepository.getTexture(pokemon.species.resourceIdentifier, pokemon.aspects, 0F))
|
||||
val tint = item.tint(stack)
|
||||
val vertexConsumer: VertexConsumer = vertexConsumers.getBuffer(renderLayer)
|
||||
val scale = 0.25f
|
||||
|
||||
matrices.push()
|
||||
matrices.scale(1f, -1f, -1f)
|
||||
matrices.translate(0.5f, -0.69f, -0.5f)
|
||||
matrices.scale(scale, scale, scale)
|
||||
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(yRot))
|
||||
|
||||
model.setupAnimStateless(PoseType.PROFILE)
|
||||
|
||||
model.withLayerContext(vertexConsumers, null, PokemonModelRepository.getLayers(pokemon.species.resourceIdentifier, pokemon.aspects)) {
|
||||
model.render(matrices, vertexConsumer, light, OverlayTexture.DEFAULT_UV, tint.x, tint.y, tint.z, tint.w)
|
||||
}
|
||||
|
||||
matrices.pop()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val mobHeads = listOf<Item>(
|
||||
Items.SKELETON_SKULL,
|
||||
Items.WITHER_SKELETON_SKULL,
|
||||
Items.ZOMBIE_HEAD,
|
||||
Items.PIGLIN_HEAD,
|
||||
Items.PLAYER_HEAD,
|
||||
Items.DRAGON_HEAD,
|
||||
Items.CREEPER_HEAD
|
||||
)
|
||||
|
||||
private fun getPositioningType(stack: ItemStack, world: World) = when {
|
||||
mobHeads.contains(stack.item) -> PositioningType.MOB_HEAD
|
||||
stack.item is BedItem -> PositioningType.BED
|
||||
stack.item is BannerItem -> PositioningType.BANNER
|
||||
stack.item is PokeBallItem -> PositioningType.POKE_BALL
|
||||
stack.item == CobblemonItems.RELIC_COIN_POUCH -> PositioningType.COIN_POUCH
|
||||
stack.item == CobblemonItems.PASTURE -> PositioningType.PASTURE
|
||||
stack.item == CobblemonItems.POKEMON_MODEL -> PositioningType.ITEM_MODEL
|
||||
stack.item == Items.SHIELD -> PositioningType.SHIELD
|
||||
stack.item == Items.DECORATED_POT -> PositioningType.MOB_HEAD
|
||||
MinecraftClient.getInstance().itemRenderer.getModel(stack, world, null, 0).hasDepth() -> PositioningType.BLOCK_MODEL
|
||||
else -> PositioningType.ITEM_MODEL
|
||||
}
|
||||
}
|
||||
|
||||
private enum class PositioningType(
|
||||
val scaleX: Float, val scaleY: Float, val scaleZ: Float,
|
||||
val transX: Float, val transY: Float, val transZ: Float,
|
||||
val rotY: Float = 0f
|
||||
) {
|
||||
POKE_BALL(1f, 1f, 1f, 0f, 0.04f, 0f),
|
||||
BLOCK_MODEL(1f, 1f, 1f, 0f, -0.15f, 0f),
|
||||
ITEM_MODEL(1f, 1f, 1f, 0f, 0.04f, 0f),
|
||||
BED(1f, 1f, 1f, 0f, -0.02f, 0f),
|
||||
BANNER(1f, 1f, 1f, 0f, -0.02f, 0f, 180f),
|
||||
MOB_HEAD(1f, 1f, 1f, 0f, -0.025f, 0f, 180f),
|
||||
SHIELD(1f, 1f, 1f, 0f, -0.045f, 0f, 180f),
|
||||
PASTURE(1f, 1f, 1f, 0f, 0.0375f, 0f),
|
||||
COIN_POUCH(1f, 1f, 1f, 0f, 0.415f, 0f)
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -61,17 +61,17 @@ class RestorationTankRenderer(ctx: BlockEntityRendererFactory.Context) : BlockEn
|
||||
matrices.pop()
|
||||
}
|
||||
val fillLevel = struct.fillLevel
|
||||
if (fillLevel == 0 && struct.createdPokemon == null) {
|
||||
if (fillLevel == 0 && !struct.hasCreatedPokemon) {
|
||||
return
|
||||
}
|
||||
|
||||
if (struct.isRunning() or (struct.createdPokemon != null)) renderFetus(entity, tickDelta, matrices, vertexConsumers, light, overlay)
|
||||
if (struct.isRunning() or (struct.hasCreatedPokemon)) renderFetus(entity, tickDelta, matrices, vertexConsumers, light, overlay)
|
||||
|
||||
matrices.push()
|
||||
val transparentBuffer = vertexConsumers.getBuffer(RenderLayer.getTranslucent())
|
||||
|
||||
val fluidModel = if (struct.isRunning()) FLUID_MODELS[8]
|
||||
else if (struct.createdPokemon != null) FLUID_MODELS[7]
|
||||
else if (struct.hasCreatedPokemon) FLUID_MODELS[7]
|
||||
else FLUID_MODELS[fillLevel.coerceAtMost(FLUID_MODELS.size - 1) - 1]
|
||||
fluidModel.getQuads(entity.cachedState, null, entity.world?.random).forEach { quad ->
|
||||
transparentBuffer?.quad(matrices.peek(), quad, 0.75f, 0.75f, 0.75f, light, OverlayTexture.DEFAULT_UV)
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ class JsonPose<T : Entity>(model: PoseableEntityModel<T>, json: JsonObject) {
|
||||
it.environment.getQueryStruct().addFunctions(model.functions.functions)
|
||||
}
|
||||
|
||||
val condition: ExpressionLike = json.singularToPluralList("condition").get("condition")?.normalizeToArray()?.map { it.asString }?.asExpressionLike() ?: "true".asExpressionLike()
|
||||
val condition: ExpressionLike = json.singularToPluralList("condition").get("conditions")?.normalizeToArray()?.map { it.asString }?.asExpressionLike() ?: "true".asExpressionLike()
|
||||
|
||||
val poseName = json.get("poseName")?.asString ?: "pose"
|
||||
val poseTypes = (json.get("poseTypes")?.asJsonArray?.map { name ->
|
||||
|
||||
+29
-3
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.client.render.models.blockbench
|
||||
import com.cobblemon.mod.common.client.render.MatrixWrapper
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
|
||||
import net.minecraft.client.util.math.MatrixStack
|
||||
import net.minecraft.entity.Entity
|
||||
|
||||
/**
|
||||
* A storage of locator information for a model, attached to its [joint].
|
||||
@@ -68,20 +69,45 @@ class LocatorAccess(
|
||||
* Updates all of the locator states with the position at this current frame.
|
||||
* This is the same logic as ModelPart uses, that's why we reuse ModelPart#rotate.
|
||||
*/
|
||||
fun update(matrixStack: MatrixStack, state: MutableMap<String, MatrixWrapper>) {
|
||||
fun update(matrixStack: MatrixStack, entity: Entity, scale: Float, state: MutableMap<String, MatrixWrapper>, isRoot: Boolean = false) {
|
||||
matrixStack.push()
|
||||
joint.transform(matrixStack)
|
||||
|
||||
if (isRoot) {
|
||||
matrixStack.push()
|
||||
matrixStack.scale(-1F, -1F, 1F)
|
||||
state.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
matrixStack.pop()
|
||||
|
||||
// Put in an approximation of the target locator. If the model has one defined,
|
||||
// this will be overridden.
|
||||
matrixStack.push()
|
||||
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale, -entity.width * 0.6 / scale)
|
||||
matrixStack.scale(-1F, -1F, 1F)
|
||||
state.getOrPut("target") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
state.getOrPut("special_attack") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
matrixStack.pop()
|
||||
|
||||
// If we have the entity, put in a "middle" locator for center of mass
|
||||
// this will be overridden.
|
||||
matrixStack.push()
|
||||
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale, 0.0)
|
||||
matrixStack.scale(-1F, -1F, 1F)
|
||||
state.getOrPut("middle") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
matrixStack.pop()
|
||||
|
||||
}
|
||||
|
||||
for ((name, locator) in locators) {
|
||||
matrixStack.push()
|
||||
locator.transform(matrixStack)
|
||||
matrixStack.scale(1F, -1F, 1F)
|
||||
matrixStack.scale(-1F, -1F, 1F)
|
||||
state.getOrPut(name) { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
matrixStack.pop()
|
||||
}
|
||||
|
||||
children.forEach {
|
||||
it.update(matrixStack, state)
|
||||
it.update(matrixStack, entity, scale, state, isRoot = false)
|
||||
}
|
||||
|
||||
matrixStack.pop()
|
||||
|
||||
+36
-15
@@ -261,6 +261,41 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
)
|
||||
)
|
||||
}
|
||||
.addFunction("bedrock_primary_quirk") { params ->
|
||||
val animationGroup = params.getString(0)
|
||||
val animationNames = params.get<MoValue>(1)?.let { if (it is ArrayStruct) it.map.values.map { it.asString() } else listOf(it.asString()) } ?: listOf()
|
||||
val minSeconds = params.getDoubleOrNull(2) ?: 8F
|
||||
val maxSeconds = params.getDoubleOrNull(3) ?: 30F
|
||||
val loopTimes = params.getDoubleOrNull(4)?.toInt() ?: 1
|
||||
val excludedLabels = mutableSetOf<String>()
|
||||
var curve: WaveFunction = { t ->
|
||||
if (t < 0.1) {
|
||||
t * 10
|
||||
} else if (t < 0.9) {
|
||||
1F
|
||||
} else {
|
||||
1F
|
||||
}
|
||||
}
|
||||
for (index in 5 until params.params.size) {
|
||||
val param = params.get<MoValue>(index)
|
||||
if (param is ObjectValue<*>) {
|
||||
curve = param.obj as WaveFunction
|
||||
continue
|
||||
}
|
||||
|
||||
val label = params.getString(index) ?: continue
|
||||
excludedLabels.add(label)
|
||||
}
|
||||
ObjectValue(
|
||||
quirk(
|
||||
secondsBetweenOccurrences = minSeconds.toFloat() to maxSeconds.toFloat(),
|
||||
condition = { true },
|
||||
loopTimes = 1..loopTimes,
|
||||
animation = { PrimaryAnimation(bedrockStateful(animationGroup, animationNames.random()), excludedLabels = excludedLabels, curve = curve) }
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Transient
|
||||
val runtime = MoLangRuntime().setup().setupClient().also { it.environment.getQueryStruct().addFunctions(functions.functions) }
|
||||
@@ -788,7 +823,6 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
matrixStack.push()
|
||||
matrixStack.scale(-1F, -1F, 1F)
|
||||
scale = entity.pokemon.form.baseScale * entity.pokemon.scaleModifier * (entity.delegate as PokemonClientDelegate).entityScaleModifier
|
||||
|
||||
matrixStack.scale(scale, scale, scale)
|
||||
} else if (entity is EmptyPokeBallEntity) {
|
||||
matrixStack.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(entity.yaw))
|
||||
@@ -803,27 +837,14 @@ abstract class PoseableEntityModel<T : Entity>(
|
||||
matrixStack.scale(1F, -1F, 1F)
|
||||
}
|
||||
|
||||
matrixStack.push()
|
||||
matrixStack.scale(1F, -1F, 1F)
|
||||
val states = state.locatorStates
|
||||
states.getOrPut("root") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
matrixStack.pop()
|
||||
|
||||
if (isForLivingEntityRenderer) {
|
||||
// Standard living entity offset, only God knows why Mojang did this.
|
||||
matrixStack.translate(0.0, -1.5, 0.0)
|
||||
}
|
||||
|
||||
// If we have the entity, put in an approximation of the target locator. If the model has one defined,
|
||||
// this will be overridden.
|
||||
matrixStack.push()
|
||||
matrixStack.translate(0.0, -entity.boundingBox.yLength / 2.0 / scale + 1.5F, -entity.width * 0.6 / scale)
|
||||
matrixStack.scale(1F, -1F, 1F)
|
||||
states.getOrPut("target") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
states.getOrPut("special_attack") { MatrixWrapper() }.updateMatrix(matrixStack.peek().positionMatrix)
|
||||
matrixStack.pop()
|
||||
|
||||
locatorAccess.update(matrixStack, states)
|
||||
locatorAccess.update(matrixStack, entity, scale, states, isRoot = true)
|
||||
}
|
||||
|
||||
fun ModelPart.translation(
|
||||
|
||||
+1
@@ -135,6 +135,7 @@ abstract class PoseableEntityState<T : Entity> : Schedulable {
|
||||
|
||||
val storm = ParticleStorm(
|
||||
effect = effect,
|
||||
entity = entity,
|
||||
matrixWrapper = matrixWrapper,
|
||||
world = world,
|
||||
runtime = particleRuntime,
|
||||
|
||||
+4
-1
@@ -39,6 +39,7 @@ import com.google.gson.annotations.SerializedName
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import java.lang.reflect.Type
|
||||
import java.util.function.Supplier
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
/**
|
||||
@@ -49,7 +50,8 @@ import net.minecraft.util.math.Vec3d
|
||||
* @author Hiroku
|
||||
* @since August 7th, 2022
|
||||
*/
|
||||
class JsonPokemonPoseableModel(override val rootPart: Bone) : PokemonPoseableModel(), HeadedFrame {
|
||||
class JsonPokemonPoseableModel(rootPart: Bone) : PokemonPoseableModel(), HeadedFrame {
|
||||
override val rootPart = (rootPart as ModelPart).children.entries.first().let { rootPart.registerChildWithAllChildren(it.key) }
|
||||
companion object {
|
||||
val gson = GsonBuilder()
|
||||
.setPrettyPrinting()
|
||||
@@ -130,6 +132,7 @@ class JsonPokemonPoseableModel(override val rootPart: Bone) : PokemonPoseableMod
|
||||
override fun createInstance(type: Type): JsonPokemonPoseableModel {
|
||||
return JsonPokemonPoseableModel(modelPart!!).also {
|
||||
model = it
|
||||
modelPart as ModelPart
|
||||
it.loadAllNamedChildren(modelPart!!)
|
||||
}
|
||||
}
|
||||
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.render.models.blockbench.pokemon.gen2
|
||||
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.BipedWalkAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
import net.minecraft.client.model.ModelPart
|
||||
import net.minecraft.util.math.Vec3d
|
||||
|
||||
class SmeargleModel(root: ModelPart) : PokemonPoseableModel(), HeadedFrame, BipedFrame {
|
||||
override val rootPart = root.registerChildWithAllChildren("smeargle")
|
||||
override val head = getPart("head")
|
||||
|
||||
/* No arm swinging because it's holding its tail */
|
||||
override val leftLeg = getPart("leg_left")
|
||||
override val rightLeg = getPart("leg_right")
|
||||
|
||||
override var portraitScale = 1.29F
|
||||
override var portraitTranslation = Vec3d(-0.03, 0.96, 0.0)
|
||||
|
||||
override var profileScale = 0.67F
|
||||
override var profileTranslation = Vec3d(0.05, 0.8, 0.0)
|
||||
|
||||
lateinit var standing: PokemonPose
|
||||
lateinit var walk: PokemonPose
|
||||
|
||||
//override val cryAnimation = CryProvider { _, _ -> bedrockStateful("smeargle", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("smeargle", "blink") }
|
||||
standing = registerPose(
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES + PoseType.SLEEP,
|
||||
quirks = arrayOf(blink),
|
||||
idleAnimations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("smeargle", "ground_idle")
|
||||
)
|
||||
)
|
||||
|
||||
walk = registerPose(
|
||||
poseName = "walk",
|
||||
poseTypes = PoseType.MOVING_POSES,
|
||||
quirks = arrayOf(blink),
|
||||
idleAnimations = arrayOf(
|
||||
singleBoneLook(),
|
||||
bedrock("smeargle", "ground_idle"),
|
||||
BipedWalkAnimation(this, periodMultiplier = 0.6F, amplitudeMultiplier = 0.9F)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
-1
@@ -47,7 +47,6 @@ class ShedinjaModel (root: ModelPart) : PokemonPoseableModel() {
|
||||
poseName = "standing",
|
||||
poseTypes = PoseType.UI_POSES + PoseType.STAND,
|
||||
transformTicks = 10,
|
||||
condition = { !it.isBattling },
|
||||
quirks = arrayOf(blink),
|
||||
idleAnimations = arrayOf(
|
||||
bedrock("shedinja", "ground_idle")
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import net.minecraft.util.math.Vec3d
|
||||
class WailordModel(root: ModelPart) : PokemonPoseableModel() {
|
||||
override val rootPart = root.registerChildWithAllChildren("wailord")
|
||||
override var portraitScale = 0.45F
|
||||
override var portraitTranslation = Vec3d(-0.38, 0.8, 0.0)
|
||||
override var portraitTranslation = Vec3d(-0.38, 0.8, 6.69)
|
||||
|
||||
override var profileScale = 0.25F
|
||||
override var profileTranslation = Vec3d(0.0, 1.2, -10.0)
|
||||
|
||||
+1
@@ -36,6 +36,7 @@ class ConkeldurrModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, B
|
||||
lateinit var battleidle: PokemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { _, _ -> bedrockStateful("conkeldurr", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("conkeldurr", "blink") }
|
||||
|
||||
|
||||
+3
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFr
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.ModelFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
@@ -40,6 +41,8 @@ class GolettModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame, Biped
|
||||
lateinit var walk: PokemonPose
|
||||
lateinit var sleep: PokemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { _, _ -> bedrockStateful("golett", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("golett", "blink") }
|
||||
standing = registerPose(
|
||||
|
||||
+3
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntitySt
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.StatefulAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.frame.ModelFrame
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPose
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPoseableModel
|
||||
import com.cobblemon.mod.common.entity.PoseType
|
||||
@@ -33,6 +34,8 @@ class GolurkModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
lateinit var walk: PokemonPose
|
||||
lateinit var sleep: PokemonPose
|
||||
|
||||
override val cryAnimation = CryProvider { _, _ -> bedrockStateful("golurk", "cry") }
|
||||
|
||||
override fun registerPoses() {
|
||||
val blink = quirk { bedrockStateful("golurk", "blink") }
|
||||
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ class TyrantrumModel (root: ModelPart) : PokemonPoseableModel(), HeadedFrame {
|
||||
|
||||
override val head = getPart("head")
|
||||
|
||||
override var portraitTranslation = Vec3d(-0.86, 2.0, 0.0)
|
||||
override var portraitTranslation = Vec3d(-0.86, 2.0, 5.0)
|
||||
override var portraitScale = 0.59F
|
||||
|
||||
override var profileTranslation = Vec3d(0.02, 1.28, -10.0)
|
||||
|
||||
+1
@@ -714,6 +714,7 @@ object PokemonModelRepository : VaryingModelRepository<PokemonEntity, PokemonPos
|
||||
inbuilt("rowlet_hisui_bias", ::RowletHisuiBiasModel)
|
||||
inbuilt("dartrix_hisui_bias", ::DartrixHisuiBiasModel)
|
||||
inbuilt("decidueye_hisuian", ::DecidueyeHisuianModel)
|
||||
inbuilt("smeargle", ::SmeargleModel)
|
||||
}
|
||||
|
||||
override fun loadJsonPoser(json: String): (Bone) -> PokemonPoseableModel {
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.mojang.brigadier.context.CommandContext
|
||||
import net.minecraft.server.command.CommandManager
|
||||
import net.minecraft.server.command.CommandManager.literal
|
||||
import net.minecraft.server.command.ServerCommandSource
|
||||
import net.minecraft.text.Text
|
||||
|
||||
/**
|
||||
* Spawn Pokemon From Surrounding Pool
|
||||
@@ -84,14 +83,15 @@ object SpawnPokemonFromPool {
|
||||
|
||||
val spawnAction = result.second.doSpawn(ctx = result.first)
|
||||
|
||||
spawnAction.future.thenApply {
|
||||
if (it is EntitySpawnResult) {
|
||||
for (entity in it.entities) {
|
||||
player.sendMessage(commandLang("spawnpokemonfrompool.success", entity.displayName).green())
|
||||
spawnAction.future
|
||||
.thenApply {
|
||||
if (it is EntitySpawnResult) {
|
||||
for (entity in it.entities) {
|
||||
player.sendMessage(commandLang("spawnpokemonfrompool", entity.displayName).green())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
spawnAction.complete()
|
||||
spawnsTriggered++
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.abilities.Abilities
|
||||
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker.after
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.taskBuilder
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
@@ -25,10 +26,13 @@ import com.cobblemon.mod.common.battles.BattleSide
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
|
||||
import com.cobblemon.mod.common.trade.ActiveTrade
|
||||
import com.cobblemon.mod.common.trade.DummyTradeParticipant
|
||||
import com.cobblemon.mod.common.trade.PlayerTradeParticipant
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.party
|
||||
import com.cobblemon.mod.common.util.toPokemon
|
||||
import com.google.gson.GsonBuilder
|
||||
@@ -61,6 +65,22 @@ object TestCommand {
|
||||
}
|
||||
|
||||
try {
|
||||
val player = context.source.entity as ServerPlayerEntity
|
||||
val evolutionEntity = GenericBedrockEntity(world = player.world)
|
||||
evolutionEntity?.apply {
|
||||
category = cobblemonResource("evolution")
|
||||
colliderHeight = 1.5F
|
||||
colliderWidth = 1.5F
|
||||
scale = 1F
|
||||
syncAge = true // Otherwise particle animation will be starting from zero even if you come along partway through
|
||||
setPosition(player.x, player.y, player.z + 4)
|
||||
}
|
||||
player.world.spawnEntity(evolutionEntity)
|
||||
after(seconds = 0.5F) {
|
||||
player.sendPacket(PlayPoseableAnimationPacket(evolutionEntity.id, setOf("evolution:animation.evolution.evolution"), emptySet()))
|
||||
}
|
||||
|
||||
|
||||
// readBerryDataFromCSV()
|
||||
|
||||
// this.testClosestBattle(context)
|
||||
|
||||
@@ -171,9 +171,6 @@ class CobblemonConfig {
|
||||
@NodeCategory(Category.Spawning)
|
||||
var baseApricornTreeGenerationChance = 0.1F
|
||||
|
||||
@NodeCategory(Category.Pokemon)
|
||||
var ninjaskCreatesShedinja = true
|
||||
|
||||
@NodeCategory(Category.Pokemon)
|
||||
var displayEntityLevelLabel = true
|
||||
|
||||
|
||||
@@ -21,13 +21,15 @@ import net.minecraft.util.StringIdentifiable
|
||||
*/
|
||||
enum class CobblemonBoatType(val mountedOffset: Double) : StringIdentifiable {
|
||||
|
||||
APRICORN(-0.1);
|
||||
APRICORN(-0.1),
|
||||
SACCHARINE(-.01);
|
||||
|
||||
/**
|
||||
* The base [Item] form of this [CobblemonBoatType].
|
||||
*/
|
||||
val boatItem: Item get() = when (this) {
|
||||
APRICORN -> CobblemonItems.APRICORN_BOAT
|
||||
SACCHARINE -> CobblemonItems.SACCHARINE_BOAT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -35,6 +37,7 @@ enum class CobblemonBoatType(val mountedOffset: Double) : StringIdentifiable {
|
||||
*/
|
||||
val chestBoatItem: Item get() = when (this) {
|
||||
APRICORN -> CobblemonItems.APRICORN_CHEST_BOAT
|
||||
SACCHARINE -> CobblemonItems.SACCHARINE_CHEST_BOAT
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,6 +45,7 @@ enum class CobblemonBoatType(val mountedOffset: Double) : StringIdentifiable {
|
||||
*/
|
||||
val baseBlock: Block get() = when (this) {
|
||||
APRICORN -> CobblemonBlocks.APRICORN_PLANKS
|
||||
SACCHARINE -> CobblemonBlocks.SACCHARINE_PLANKS
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -49,6 +53,7 @@ enum class CobblemonBoatType(val mountedOffset: Double) : StringIdentifiable {
|
||||
*/
|
||||
val woodType: WoodType get() = when (this) {
|
||||
APRICORN -> CobblemonBlocks.APRICORN_WOOD_TYPE
|
||||
SACCHARINE -> CobblemonBlocks.SACCHARINE_WOOD_TYPE
|
||||
}
|
||||
|
||||
override fun asString(): String = this.name.lowercase()
|
||||
|
||||
+6
-4
@@ -20,6 +20,7 @@ import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
|
||||
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
|
||||
import com.cobblemon.mod.common.api.pokeball.PokeBalls
|
||||
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
@@ -57,6 +58,7 @@ import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.hit.BlockHitResult
|
||||
import net.minecraft.util.hit.EntityHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
@@ -189,7 +191,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
|
||||
val owner = owner
|
||||
|
||||
if (!pokemonEntity.pokemon.isWild()) {
|
||||
owner?.sendMessage(lang("capture.not_wild", pokemonEntity.pokemon.species.translatedName).red())
|
||||
owner?.sendMessage(lang("capture.not_wild", pokemonEntity.exposedSpecies.translatedName).red())
|
||||
return drop()
|
||||
}
|
||||
|
||||
@@ -204,7 +206,7 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
|
||||
val hitBattlePokemon = hitActor?.activePokemon?.find { it.battlePokemon?.effectedPokemon?.entity == pokemonEntity }
|
||||
|
||||
if (throwerActor == null) {
|
||||
owner.sendMessage(lang("capture.in_battle", pokemonEntity.pokemon.species.translatedName).red())
|
||||
owner.sendMessage(lang("capture.in_battle", pokemonEntity.exposedSpecies.translatedName).red())
|
||||
return drop()
|
||||
}
|
||||
|
||||
@@ -230,13 +232,13 @@ class EmptyPokeBallEntity : ThrownItemEntity, Poseable, WaterDragModifier, Sched
|
||||
"capture.attempted_capture",
|
||||
throwerActor.getName(),
|
||||
pokeBall.item().name,
|
||||
pokemonEntity.pokemon.species.translatedName
|
||||
pokemonEntity.exposedSpecies.translatedName
|
||||
).yellow()
|
||||
)
|
||||
battle.sendUpdate(BattleCaptureStartPacket(pokeBall.name, aspects, hitBattlePokemon.getPNX()))
|
||||
throwerActor.forceChoose(ForcePassActionResponse())
|
||||
} else if (pokemonEntity.isBusy) {
|
||||
owner?.sendMessage(lang("capture.busy", pokemonEntity.pokemon.species.translatedName).red())
|
||||
owner?.sendMessage(lang("capture.busy", pokemonEntity.exposedSpecies.translatedName).red())
|
||||
return drop()
|
||||
} else if (owner is ServerPlayerEntity && BattleRegistry.getBattleByParticipatingPlayer(owner) != null) {
|
||||
owner.sendMessage(lang("you_in_battle").red())
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.api.reactive.ObservableSubscription
|
||||
@@ -615,129 +616,152 @@ open class PokemonEntity(
|
||||
|
||||
override fun interactMob(player: PlayerEntity, hand: Hand) : ActionResult {
|
||||
val itemStack = player.getStackInHand(hand)
|
||||
val colorFeatureType = SpeciesFeatures.getFeaturesFor(pokemon.species).find { it is ChoiceSpeciesFeatureProvider && DataKeys.CAN_BE_COLORED in it.keys }
|
||||
val colorFeature = pokemon.getFeature<StringSpeciesFeature>(DataKeys.CAN_BE_COLORED)
|
||||
|
||||
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
|
||||
this.sheared(SoundCategory.PLAYERS)
|
||||
this.emitGameEvent(GameEvent.SHEAR, player)
|
||||
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
else if (itemStack.isOf(Items.BUCKET)) {
|
||||
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
|
||||
player.playSound(SoundEvents.ENTITY_GOAT_MILK, 1.0f, 1.0f)
|
||||
val milkBucket = ItemUsage.exchangeStack(itemStack, player, Items.MILK_BUCKET.defaultStack)
|
||||
player.setStackInHand(hand, milkBucket)
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
} else if (itemStack.isOf(Items.BOWL)) {
|
||||
if (pokemon.aspects.any() {it.contains("mooshtank")}) {
|
||||
player.playSound(SoundEvents.ENTITY_MOOSHROOM_MILK, 1.0f, 1.0f)
|
||||
// if the Mooshtank ate a Flower beforehand
|
||||
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any() {it.contains("mooshtank-brown")}) {
|
||||
var effect: StatusEffect? = null
|
||||
var duration = 0
|
||||
if (ownerUuid == player.uuid || ownerUuid == null) {
|
||||
if (itemStack.isOf(Items.SHEARS) && this.isShearable) {
|
||||
this.sheared(SoundCategory.PLAYERS)
|
||||
this.emitGameEvent(GameEvent.SHEAR, player)
|
||||
itemStack.damage(1, player) { it.sendToolBreakStatus(hand) }
|
||||
return ActionResult.SUCCESS
|
||||
} else if (itemStack.isOf(Items.BUCKET)) {
|
||||
if (pokemon.getFeature<FlagSpeciesFeature>(DataKeys.CAN_BE_MILKED) != null) {
|
||||
player.playSound(SoundEvents.ENTITY_GOAT_MILK, 1.0f, 1.0f)
|
||||
val milkBucket = ItemUsage.exchangeStack(itemStack, player, Items.MILK_BUCKET.defaultStack)
|
||||
player.setStackInHand(hand, milkBucket)
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
} else if (itemStack.isOf(Items.BOWL)) {
|
||||
if (pokemon.aspects.any { it.contains("mooshtank") }) {
|
||||
player.playSound(SoundEvents.ENTITY_MOOSHROOM_MILK, 1.0f, 1.0f)
|
||||
// if the Mooshtank ate a Flower beforehand
|
||||
if (pokemon.lastFlowerFed != ItemStack.EMPTY && pokemon.aspects.any() { it.contains("mooshtank-brown") }) {
|
||||
var effect: StatusEffect? = null
|
||||
var duration = 0
|
||||
|
||||
if (pokemon.lastFlowerFed.isOf(Items.ALLIUM)) {
|
||||
effect = StatusEffect.byRawId(12) // Fire Resistance
|
||||
duration = 80 // 4 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.AZURE_BLUET)) {
|
||||
effect = StatusEffect.byRawId(15) // Blindness
|
||||
duration = 160 // 8 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.BLUE_ORCHID) || pokemon.lastFlowerFed.isOf(Items.DANDELION)) {
|
||||
effect = StatusEffect.byRawId(23) // Saturation
|
||||
duration = 7 // .35 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.CORNFLOWER)) {
|
||||
effect = StatusEffect.byRawId(8) // Jump Boost
|
||||
duration = 120 // 6 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.LILY_OF_THE_VALLEY)) {
|
||||
effect = StatusEffect.byRawId(19) // Poison
|
||||
duration = 240 // 12 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.OXEYE_DAISY)) {
|
||||
effect = StatusEffect.byRawId(10) // Regeneration
|
||||
duration = 160 // 8 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.POPPY) || pokemon.lastFlowerFed.isOf(Items.TORCHFLOWER)) {
|
||||
effect = StatusEffect.byRawId(16) // Night Vision
|
||||
duration = 100 // 5 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.PINK_TULIP) || pokemon.lastFlowerFed.isOf(Items.RED_TULIP) || pokemon.lastFlowerFed.isOf(Items.WHITE_TULIP) || pokemon.lastFlowerFed.isOf(Items.ORANGE_TULIP)) {
|
||||
effect = StatusEffect.byRawId(18) // Weakness
|
||||
duration = 180 // 9 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.WITHER_ROSE)) {
|
||||
effect = StatusEffect.byRawId(20) // Wither
|
||||
duration = 160 // 8 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(CobblemonItems.PEP_UP_FLOWER)) {
|
||||
effect = StatusEffect.byRawId(25) // Levitation
|
||||
duration = 160 // 8 seconds
|
||||
if (pokemon.lastFlowerFed.isOf(Items.ALLIUM)) {
|
||||
effect = StatusEffect.byRawId(12) // Fire Resistance
|
||||
duration = 80 // 4 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.AZURE_BLUET)) {
|
||||
effect = StatusEffect.byRawId(15) // Blindness
|
||||
duration = 160 // 8 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.BLUE_ORCHID) || pokemon.lastFlowerFed.isOf(Items.DANDELION)) {
|
||||
effect = StatusEffect.byRawId(23) // Saturation
|
||||
duration = 7 // .35 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.CORNFLOWER)) {
|
||||
effect = StatusEffect.byRawId(8) // Jump Boost
|
||||
duration = 120 // 6 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.LILY_OF_THE_VALLEY)) {
|
||||
effect = StatusEffect.byRawId(19) // Poison
|
||||
duration = 240 // 12 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.OXEYE_DAISY)) {
|
||||
effect = StatusEffect.byRawId(10) // Regeneration
|
||||
duration = 160 // 8 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.POPPY) || pokemon.lastFlowerFed.isOf(Items.TORCHFLOWER)) {
|
||||
effect = StatusEffect.byRawId(16) // Night Vision
|
||||
duration = 100 // 5 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.PINK_TULIP) || pokemon.lastFlowerFed.isOf(Items.RED_TULIP) || pokemon.lastFlowerFed.isOf(
|
||||
Items.WHITE_TULIP
|
||||
) || pokemon.lastFlowerFed.isOf(Items.ORANGE_TULIP)
|
||||
) {
|
||||
effect = StatusEffect.byRawId(18) // Weakness
|
||||
duration = 180 // 9 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(Items.WITHER_ROSE)) {
|
||||
effect = StatusEffect.byRawId(20) // Wither
|
||||
duration = 160 // 8 seconds
|
||||
} else if (pokemon.lastFlowerFed.isOf(CobblemonItems.PEP_UP_FLOWER)) {
|
||||
effect = StatusEffect.byRawId(25) // Levitation
|
||||
duration = 160 // 8 seconds
|
||||
}
|
||||
|
||||
|
||||
// modify the suspicious stew with the effect
|
||||
val susStewStack = Items.SUSPICIOUS_STEW.defaultStack
|
||||
SuspiciousStewItem.addEffectToStew(susStewStack, effect, duration)
|
||||
val susStewEffect = ItemUsage.exchangeStack(itemStack, player, susStewStack)
|
||||
//give player modified Suspicious Stew
|
||||
player.setStackInHand(hand, susStewEffect)
|
||||
// reset the flower fed state
|
||||
pokemon.lastFlowerFed = ItemStack.EMPTY
|
||||
return ActionResult.success(world.isClient)
|
||||
} else {
|
||||
val mushroomStew = ItemUsage.exchangeStack(itemStack, player, Items.MUSHROOM_STEW.defaultStack)
|
||||
player.setStackInHand(hand, mushroomStew)
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
|
||||
|
||||
// modify the suspicious stew with the effect
|
||||
val susStewStack = Items.SUSPICIOUS_STEW.defaultStack
|
||||
SuspiciousStewItem.addEffectToStew(susStewStack, effect, duration)
|
||||
val susStewEffect = ItemUsage.exchangeStack(itemStack, player, susStewStack)
|
||||
//give player modified Suspicious Stew
|
||||
player.setStackInHand(hand, susStewEffect)
|
||||
// reset the flower fed state
|
||||
pokemon.lastFlowerFed = ItemStack.EMPTY
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
else {
|
||||
val mushroomStew = ItemUsage.exchangeStack(itemStack, player, Items.MUSHROOM_STEW.defaultStack)
|
||||
player.setStackInHand(hand, mushroomStew)
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
// Flowers used on brown MooshTanks
|
||||
else if (itemStack.isOf(Items.ALLIUM) ||
|
||||
itemStack.isOf(Items.AZURE_BLUET) ||
|
||||
itemStack.isOf(Items.BLUE_ORCHID) ||
|
||||
itemStack.isOf(Items.DANDELION) ||
|
||||
itemStack.isOf(Items.CORNFLOWER) ||
|
||||
itemStack.isOf(Items.LILY_OF_THE_VALLEY) ||
|
||||
itemStack.isOf(Items.OXEYE_DAISY) ||
|
||||
itemStack.isOf(Items.POPPY) ||
|
||||
itemStack.isOf(Items.TORCHFLOWER) ||
|
||||
itemStack.isOf(Items.PINK_TULIP) ||
|
||||
itemStack.isOf(Items.RED_TULIP) ||
|
||||
itemStack.isOf(Items.WHITE_TULIP) ||
|
||||
itemStack.isOf(Items.ORANGE_TULIP) ||
|
||||
itemStack.isOf(Items.WITHER_ROSE) ||
|
||||
itemStack.isOf(CobblemonItems.PEP_UP_FLOWER)) {
|
||||
if (pokemon.aspects.any() {it.contains("mooshtank")}) {
|
||||
player.playSound(SoundEvents.ENTITY_MOOSHROOM_EAT, 1.0f, 1.0f)
|
||||
pokemon.lastFlowerFed = itemStack
|
||||
// Flowers used on brown MooshTanks
|
||||
else if (itemStack.isOf(Items.ALLIUM) ||
|
||||
itemStack.isOf(Items.AZURE_BLUET) ||
|
||||
itemStack.isOf(Items.BLUE_ORCHID) ||
|
||||
itemStack.isOf(Items.DANDELION) ||
|
||||
itemStack.isOf(Items.CORNFLOWER) ||
|
||||
itemStack.isOf(Items.LILY_OF_THE_VALLEY) ||
|
||||
itemStack.isOf(Items.OXEYE_DAISY) ||
|
||||
itemStack.isOf(Items.POPPY) ||
|
||||
itemStack.isOf(Items.TORCHFLOWER) ||
|
||||
itemStack.isOf(Items.PINK_TULIP) ||
|
||||
itemStack.isOf(Items.RED_TULIP) ||
|
||||
itemStack.isOf(Items.WHITE_TULIP) ||
|
||||
itemStack.isOf(Items.ORANGE_TULIP) ||
|
||||
itemStack.isOf(Items.WITHER_ROSE) ||
|
||||
itemStack.isOf(CobblemonItems.PEP_UP_FLOWER)
|
||||
) {
|
||||
if (pokemon.aspects.any { it.contains("mooshtank") }) {
|
||||
player.playSound(SoundEvents.ENTITY_MOOSHROOM_EAT, 1.0f, 1.0f)
|
||||
pokemon.lastFlowerFed = itemStack
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
} else if (!player.isSneaking && player.uuid == ownerUuid && (itemStack.isOf(CobblemonItems.RELIC_COIN)
|
||||
|| itemStack.isOf(CobblemonItems.RELIC_COIN_POUCH)
|
||||
|| itemStack.isOf(CobblemonItems.RELIC_COIN_SACK)
|
||||
|| itemStack.isOf(Items.NETHERITE_SCRAP)
|
||||
|| itemStack.isOf(Items.NETHERITE_INGOT)
|
||||
|| itemStack.isOf(Items.NETHERITE_BLOCK))
|
||||
) {
|
||||
if (GimmighoulStashHandler.interactMob(player, hand, pokemon)) {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
} else if (itemStack.item is DyeItem && colorFeatureType != null) {
|
||||
val currentColor = colorFeature?.value ?: ""
|
||||
val item = itemStack.item as DyeItem
|
||||
if (!item.color.name.equals(currentColor, ignoreCase = true)) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
if (colorFeature != null) {
|
||||
colorFeature.value = item.color.name.lowercase()
|
||||
this.pokemon.markFeatureDirty(colorFeature)
|
||||
} else {
|
||||
val newColorFeature =
|
||||
StringSpeciesFeature(DataKeys.CAN_BE_COLORED, item.color.name.lowercase())
|
||||
this.pokemon.features.add(newColorFeature)
|
||||
this.pokemon.anyChangeObservable.emit(pokemon)
|
||||
}
|
||||
|
||||
this.pokemon.updateAspects()
|
||||
if (!player.isCreative) {
|
||||
itemStack.decrement(1)
|
||||
}
|
||||
}
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
} else if (itemStack.item.equals(Items.WATER_BUCKET) && colorFeatureType != null) {
|
||||
if (player is ServerPlayerEntity) {
|
||||
if (colorFeature != null) {
|
||||
if (!player.isCreative) {
|
||||
itemStack.decrement(1)
|
||||
player.giveOrDropItemStack(Items.BUCKET.defaultStack)
|
||||
}
|
||||
colorFeature.value = ""
|
||||
this.pokemon.markFeatureDirty(colorFeature)
|
||||
this.pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
return ActionResult.success(world.isClient)
|
||||
}
|
||||
} else if(!player.isSneaking && (itemStack.isOf(CobblemonItems.RELIC_COIN)
|
||||
|| itemStack.isOf(CobblemonItems.RELIC_COIN_POUCH)
|
||||
|| itemStack.isOf(CobblemonItems.RELIC_COIN_SACK)
|
||||
|| itemStack.isOf(Items.NETHERITE_SCRAP)
|
||||
|| itemStack.isOf(Items.NETHERITE_INGOT)
|
||||
|| itemStack.isOf(Items.NETHERITE_BLOCK))) {
|
||||
|
||||
if(GimmighoulStashHandler.interactMob(player, hand, pokemon)) {
|
||||
return ActionResult.SUCCESS
|
||||
}
|
||||
}
|
||||
else if (itemStack.item is DyeItem && colorFeature != null) {
|
||||
val item = itemStack.item as DyeItem
|
||||
if (item.color.name.lowercase() != colorFeature.value.lowercase()) {
|
||||
itemStack.decrement(1)
|
||||
colorFeature.value = item.color.name.lowercase()
|
||||
this.pokemon.markFeatureDirty(colorFeature)
|
||||
this.pokemon.updateAspects()
|
||||
}
|
||||
}
|
||||
else if (itemStack.item.equals(Items.WATER_BUCKET) && colorFeature != null) {
|
||||
itemStack.decrement(1)
|
||||
player.giveOrDropItemStack(Items.BUCKET.defaultStack)
|
||||
colorFeature.value = ""
|
||||
this.pokemon.markFeatureDirty(colorFeature)
|
||||
this.pokemon.updateAspects()
|
||||
}
|
||||
|
||||
|
||||
if (hand == Hand.MAIN_HAND && player is ServerPlayerEntity && pokemon.getOwnerPlayer() == player) {
|
||||
if (player.isSneaking) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.cobblemon.mod.common.entity.throwable
|
||||
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.mob.BlazeEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.projectile.thrown.ThrownItemEntity
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ItemStackParticleEffect
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.hit.EntityHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
import net.minecraft.world.World
|
||||
|
||||
|
||||
class ScatterBangEntity : ThrownItemEntity {
|
||||
constructor(entityType: EntityType<out ScatterBangEntity?>?, world: World?) : super(entityType, world)
|
||||
|
||||
constructor(world: World?, owner: LivingEntity?) : super(EntityType.SNOWBALL, owner, world)
|
||||
|
||||
constructor(world: World?, x: Double, y: Double, z: Double) : super(EntityType.SNOWBALL, x, y, z, world)
|
||||
|
||||
override fun getDefaultItem(): Item {
|
||||
return Items.SNOWBALL
|
||||
}
|
||||
|
||||
private val particleParameters: ParticleEffect
|
||||
get() {
|
||||
val itemStack = this.item
|
||||
return (if (itemStack.isEmpty) ParticleTypes.ITEM_SNOWBALL else ItemStackParticleEffect(
|
||||
ParticleTypes.ITEM,
|
||||
itemStack
|
||||
)) as ParticleEffect
|
||||
}
|
||||
|
||||
override fun handleStatus(status: Byte) {
|
||||
if (status.toInt() == 3) {
|
||||
val particleEffect = this.particleParameters
|
||||
|
||||
for (i in 0..7) {
|
||||
world.addParticle(particleEffect, this.x, this.y, this.z, 0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEntityHit(entityHitResult: EntityHitResult) {
|
||||
world.playSound(
|
||||
null as PlayerEntity?,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
SoundEvents.ENTITY_GENERIC_EXPLODE,
|
||||
SoundCategory.NEUTRAL,
|
||||
0.6f,
|
||||
0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f)
|
||||
)
|
||||
|
||||
super.onEntityHit(entityHitResult)
|
||||
val entity = entityHitResult.entity
|
||||
val i = if (entity is BlazeEntity) 3 else 0
|
||||
entity.damage(this.damageSources.thrown(this, this.owner), i.toFloat())
|
||||
}
|
||||
|
||||
override fun onCollision(hitResult: HitResult) {
|
||||
world.playSound(
|
||||
null as PlayerEntity?,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
SoundEvents.ENTITY_GENERIC_EXPLODE,
|
||||
SoundCategory.NEUTRAL,
|
||||
0.6f,
|
||||
0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f)
|
||||
)
|
||||
|
||||
super.onCollision(hitResult)
|
||||
if (!world.isClient) {
|
||||
world.sendEntityStatus(this, 3.toByte())
|
||||
this.discard()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.cobblemon.mod.common.entity.throwable
|
||||
|
||||
import net.minecraft.entity.EntityType
|
||||
import net.minecraft.entity.LivingEntity
|
||||
import net.minecraft.entity.mob.BlazeEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.projectile.thrown.ThrownItemEntity
|
||||
import net.minecraft.item.Item
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.particle.ItemStackParticleEffect
|
||||
import net.minecraft.particle.ParticleEffect
|
||||
import net.minecraft.particle.ParticleTypes
|
||||
import net.minecraft.sound.SoundCategory
|
||||
import net.minecraft.sound.SoundEvents
|
||||
import net.minecraft.util.hit.EntityHitResult
|
||||
import net.minecraft.util.hit.HitResult
|
||||
import net.minecraft.world.World
|
||||
|
||||
|
||||
class StickyGlobEntity : ThrownItemEntity {
|
||||
constructor(entityType: EntityType<out StickyGlobEntity?>?, world: World?) : super(entityType, world)
|
||||
|
||||
constructor(world: World?, owner: LivingEntity?) : super(EntityType.SNOWBALL, owner, world)
|
||||
|
||||
constructor(world: World?, x: Double, y: Double, z: Double) : super(EntityType.SNOWBALL, x, y, z, world)
|
||||
|
||||
override fun getDefaultItem(): Item {
|
||||
return Items.SNOWBALL
|
||||
}
|
||||
|
||||
private val particleParameters: ParticleEffect
|
||||
get() {
|
||||
val itemStack = this.item
|
||||
return (if (itemStack.isEmpty) ParticleTypes.ITEM_SNOWBALL else ItemStackParticleEffect(
|
||||
ParticleTypes.ITEM,
|
||||
itemStack
|
||||
)) as ParticleEffect
|
||||
}
|
||||
|
||||
override fun handleStatus(status: Byte) {
|
||||
if (status.toInt() == 3) {
|
||||
val particleEffect = this.particleParameters
|
||||
|
||||
for (i in 0..7) {
|
||||
world.addParticle(particleEffect, this.x, this.y, this.z, 0.0, 0.0, 0.0)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onEntityHit(entityHitResult: EntityHitResult) {
|
||||
world.playSound(
|
||||
null as PlayerEntity?,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
SoundEvents.ENTITY_SLIME_SQUISH,
|
||||
SoundCategory.NEUTRAL,
|
||||
0.6f,
|
||||
0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f)
|
||||
)
|
||||
|
||||
super.onEntityHit(entityHitResult)
|
||||
val entity = entityHitResult.entity
|
||||
val i = if (entity is BlazeEntity) 3 else 0
|
||||
entity.damage(this.damageSources.thrown(this, this.owner), i.toFloat())
|
||||
}
|
||||
|
||||
override fun onCollision(hitResult: HitResult) {
|
||||
world.playSound(
|
||||
null as PlayerEntity?,
|
||||
this.x,
|
||||
this.y,
|
||||
this.z,
|
||||
SoundEvents.ENTITY_SLIME_SQUISH,
|
||||
SoundCategory.NEUTRAL,
|
||||
0.6f,
|
||||
0.4f / (world.getRandom().nextFloat() * 0.4f + 0.8f)
|
||||
)
|
||||
|
||||
super.onCollision(hitResult)
|
||||
if (!world.isClient) {
|
||||
world.sendEntityStatus(this, 3.toByte())
|
||||
this.discard()
|
||||
}
|
||||
}
|
||||
}
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
package com.cobblemon.mod.common.gui
|
||||
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.inventory.SimpleInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.recipe.*
|
||||
import net.minecraft.recipe.book.RecipeBookCategory
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
import net.minecraft.screen.ArrayPropertyDelegate
|
||||
import net.minecraft.screen.PropertyDelegate
|
||||
import net.minecraft.screen.ScreenHandlerType
|
||||
import net.minecraft.screen.slot.FurnaceOutputSlot
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.world.World
|
||||
|
||||
abstract class AbstractCookingPotScreenHandler(
|
||||
type: ScreenHandlerType<*>?,
|
||||
private val recipeType: RecipeType<out AbstractCookingRecipe?>,
|
||||
private val category: RecipeBookCategory,
|
||||
syncId: Int,
|
||||
playerInventory: PlayerInventory?,
|
||||
inventory: Inventory? = SimpleInventory(3),
|
||||
propertyDelegate: PropertyDelegate? = ArrayPropertyDelegate(
|
||||
4
|
||||
)
|
||||
) :
|
||||
AbstractRecipeScreenHandler<Inventory?>(type, syncId) {
|
||||
lateinit var inventory: Inventory
|
||||
lateinit var propertyDelegate: PropertyDelegate
|
||||
lateinit var world: World
|
||||
|
||||
init {
|
||||
checkSize(inventory, 3)
|
||||
checkDataCount(propertyDelegate, 4)
|
||||
if (inventory != null) {
|
||||
this.inventory = inventory
|
||||
}
|
||||
if (propertyDelegate != null) {
|
||||
this.propertyDelegate = propertyDelegate
|
||||
}
|
||||
if (playerInventory != null) {
|
||||
this.world = playerInventory.player.world
|
||||
}
|
||||
this.addSlot(Slot(inventory, 0, 56, 17))
|
||||
//this.addSlot(FurnaceFuelSlot(this, inventory, 1, 56, 53))
|
||||
if (playerInventory != null) {
|
||||
this.addSlot(FurnaceOutputSlot(playerInventory.player, inventory, 2, 116, 35))
|
||||
}
|
||||
var i = 0
|
||||
while (i < 3) {
|
||||
for (j in 0..8) {
|
||||
this.addSlot(Slot(playerInventory, j + i * 9 + 9, 8 + j * 18, 84 + i * 18))
|
||||
}
|
||||
++i
|
||||
}
|
||||
|
||||
i = 0
|
||||
while (i < 9) {
|
||||
this.addSlot(Slot(playerInventory, i, 8 + i * 18, 142))
|
||||
++i
|
||||
}
|
||||
|
||||
this.addProperties(propertyDelegate)
|
||||
}
|
||||
|
||||
override fun populateRecipeFinder(finder: RecipeMatcher) {
|
||||
if (inventory is RecipeInputProvider) {
|
||||
(inventory as RecipeInputProvider).provideRecipeInputs(finder)
|
||||
}
|
||||
}
|
||||
|
||||
override fun clearCraftingSlots() {
|
||||
getSlot(0).setStackNoCallbacks(ItemStack.EMPTY)
|
||||
getSlot(2).setStackNoCallbacks(ItemStack.EMPTY)
|
||||
}
|
||||
|
||||
override fun matches(recipe: Recipe<in Inventory?>): Boolean {
|
||||
return recipe.matches(this.inventory, this.world)
|
||||
}
|
||||
|
||||
override fun getCraftingResultSlotIndex(): Int {
|
||||
return 2
|
||||
}
|
||||
|
||||
override fun getCraftingWidth(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun getCraftingHeight(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun getCraftingSlotCount(): Int {
|
||||
return 3
|
||||
}
|
||||
|
||||
override fun canUse(player: PlayerEntity): Boolean {
|
||||
return inventory.canPlayerUse(player)
|
||||
}
|
||||
|
||||
override fun quickMove(player: PlayerEntity, slot: Int): ItemStack {
|
||||
var itemStack = ItemStack.EMPTY
|
||||
val slot2 = slots[slot]
|
||||
if (slot2 != null && slot2.hasStack()) {
|
||||
val itemStack2 = slot2.stack
|
||||
itemStack = itemStack2.copy()
|
||||
if (slot == 2) {
|
||||
if (!this.insertItem(itemStack2, 3, 39, true)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
slot2.onQuickTransfer(itemStack2, itemStack)
|
||||
} else if (slot != 1 && slot != 0) {
|
||||
/*if (this.isSmeltable(itemStack2)) {
|
||||
if (!this.insertItem(itemStack2, 0, 1, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}*/ /*else if (this.isFuel(itemStack2)) {
|
||||
if (!this.insertItem(itemStack2, 1, 2, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
}*/ if (slot >= 3 && slot < 30) {
|
||||
if (!this.insertItem(itemStack2, 30, 39, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else if (slot >= 30 && slot < 39 && !this.insertItem(itemStack2, 3, 30, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else if (!this.insertItem(itemStack2, 3, 39, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
if (itemStack2.isEmpty) {
|
||||
slot2.stack = ItemStack.EMPTY
|
||||
} else {
|
||||
slot2.markDirty()
|
||||
}
|
||||
|
||||
if (itemStack2.count == itemStack.count) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
|
||||
slot2.onTakeItem(player, itemStack2)
|
||||
}
|
||||
|
||||
return itemStack
|
||||
}
|
||||
|
||||
/*fun isSmeltable(itemStack: ItemStack): Boolean {
|
||||
return world.recipeManager.getFirstMatch(
|
||||
this.recipeType, SimpleInventory(*arrayOf(itemStack)),
|
||||
this.world
|
||||
).isPresent
|
||||
}*/
|
||||
|
||||
/*protected fun isFuel(itemStack: ItemStack?): Boolean {
|
||||
return AbstractFurnaceBlockEntity.canUseAsFuel(itemStack)
|
||||
}*/
|
||||
|
||||
val cookProgress: Int
|
||||
get() {
|
||||
val i = propertyDelegate[2]
|
||||
val j = propertyDelegate[3]
|
||||
return if (j != 0 && i != 0) i * 24 / j else 0
|
||||
}
|
||||
|
||||
val fuelProgress: Int
|
||||
get() {
|
||||
var i = propertyDelegate[1]
|
||||
if (i == 0) {
|
||||
i = 200
|
||||
}
|
||||
|
||||
return propertyDelegate[0] * 13 / i
|
||||
}
|
||||
|
||||
val isBurning: Boolean
|
||||
get() = propertyDelegate[0] > 0
|
||||
|
||||
override fun getCategory(): RecipeBookCategory {
|
||||
return this.category
|
||||
}
|
||||
|
||||
override fun canInsertIntoSlot(index: Int): Boolean {
|
||||
return index != 1
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val field_30738: Int = 0
|
||||
const val field_30739: Int = 1
|
||||
const val field_30740: Int = 2
|
||||
const val field_30741: Int = 3
|
||||
const val field_30742: Int = 4
|
||||
private const val field_30743 = 3
|
||||
private const val field_30744 = 30
|
||||
private const val field_30745 = 30
|
||||
private const val field_30746 = 39
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.cobblemon.mod.common.gui
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.mixin.invoker.ScreenHandlerTypeInvoker
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.resource.featuretoggle.FeatureFlags
|
||||
import net.minecraft.screen.ScreenHandlerType.Factory
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.ScreenHandlerContext
|
||||
import net.minecraft.screen.ScreenHandlerType
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
object CobblemonScreenHandlers {
|
||||
//val TMM_SCREEN = register(cobblemonResource("tmm_screen"), ::TMMScreenHandler)
|
||||
val COOKING_POT_SCREEN = register(cobblemonResource("cooking_pot_screen")) { syncId, playerInventory ->
|
||||
CookingPotScreenHandler(syncId, playerInventory, ScreenHandlerContext.EMPTY)
|
||||
}
|
||||
|
||||
fun <T : ScreenHandler> register(identifier: Identifier, factory: Factory<T>): ScreenHandlerType<T> {
|
||||
|
||||
val result = ScreenHandlerTypeInvoker.`cobblemon$create`(factory, FeatureFlags.VANILLA_FEATURES)
|
||||
Cobblemon.implementation.registerScreenHandlerType(identifier, result)
|
||||
return result
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
package com.cobblemon.mod.common.gui
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.block.entity.CookingPotBlockEntity
|
||||
import net.minecraft.entity.player.PlayerEntity
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.inventory.CraftingInventory
|
||||
import net.minecraft.inventory.CraftingResultInventory
|
||||
import net.minecraft.inventory.Inventory
|
||||
import net.minecraft.inventory.RecipeInputInventory
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.recipe.RecipeMatcher
|
||||
import net.minecraft.recipe.RecipeType
|
||||
import net.minecraft.recipe.book.RecipeBookCategory
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
import net.minecraft.screen.ScreenHandler
|
||||
import net.minecraft.screen.ScreenHandlerContext
|
||||
import net.minecraft.screen.slot.CraftingResultSlot
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.server.network.ServerPlayerEntity
|
||||
import net.minecraft.text.MutableText
|
||||
import net.minecraft.world.World
|
||||
|
||||
class CookingPotScreenHandler : AbstractRecipeScreenHandler<RecipeInputInventory> {
|
||||
val input: CraftingInventory
|
||||
val result: CraftingResultInventory
|
||||
val context: ScreenHandlerContext
|
||||
val player: PlayerEntity
|
||||
var inventory: Inventory? = null
|
||||
|
||||
companion object {
|
||||
const val RESULT_ID = 0
|
||||
private const val INPUT_START = 1
|
||||
private const val INPUT_END = 10
|
||||
private const val INVENTORY_START = 10
|
||||
private const val INVENTORY_END = 37
|
||||
private const val HOTBAR_START = 37
|
||||
private const val HOTBAR_END = 46
|
||||
|
||||
fun updateResult(
|
||||
handler: ScreenHandler,
|
||||
world: World,
|
||||
player: PlayerEntity,
|
||||
craftingInventory: RecipeInputInventory,
|
||||
resultInventory: CraftingResultInventory
|
||||
) {
|
||||
if (world.isClient) return
|
||||
val serverPlayerEntity = player as ServerPlayerEntity
|
||||
var itemStack = ItemStack.EMPTY
|
||||
val optional = world.server?.recipeManager?.getFirstMatch(
|
||||
RecipeType.CRAFTING, craftingInventory, world
|
||||
)
|
||||
if (optional != null) {
|
||||
if (optional.isPresent) {
|
||||
val craftingRecipe = optional.get()
|
||||
val itemStack2 = craftingRecipe.craft(craftingInventory, world.registryManager)
|
||||
if (itemStack2.isItemEnabled(world.enabledFeatures)) {
|
||||
itemStack = itemStack2
|
||||
}
|
||||
}
|
||||
}
|
||||
resultInventory.setStack(0, itemStack)
|
||||
handler.setPreviousTrackedSlot(0, itemStack)
|
||||
serverPlayerEntity.networkHandler.sendPacket(
|
||||
ScreenHandlerSlotUpdateS2CPacket(handler.syncId, handler.nextRevision(), 0, itemStack)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
constructor(syncId: Int, playerInventory: PlayerInventory, context: ScreenHandlerContext) :
|
||||
super(CobblemonScreenHandlers.COOKING_POT_SCREEN, syncId) {
|
||||
this.context = context
|
||||
this.player = playerInventory.player
|
||||
this.input = CraftingInventory(this, 3, 3)
|
||||
this.result = CraftingResultInventory()
|
||||
initializeSlots(playerInventory)
|
||||
}
|
||||
|
||||
constructor(syncId: Int, playerInventory: PlayerInventory, cookingPotInventory: CookingPotBlockEntity.CookingPotBlockInventory, cookingPotBlockEntity: CookingPotBlockEntity) :
|
||||
super(CobblemonScreenHandlers.COOKING_POT_SCREEN, syncId) {
|
||||
this.context = ScreenHandlerContext.EMPTY
|
||||
this.player = playerInventory.player
|
||||
this.input = CraftingInventory(this, 3, 3)
|
||||
this.result = CraftingResultInventory()
|
||||
this.inventory = cookingPotInventory
|
||||
initializeSlots(playerInventory)
|
||||
}
|
||||
|
||||
|
||||
private fun initializeSlots(playerInventory: PlayerInventory) {
|
||||
val craftingGridOffsetX = 14
|
||||
val craftingGridOffsetY = 10
|
||||
val craftingOutputOffsetX = 16
|
||||
val craftingOutputOffsetY = 10
|
||||
val playerInventoryOffsetX = 0
|
||||
val playerInventoryOffsetY = 16
|
||||
|
||||
addSlot(CraftingResultSlot(playerInventory.player, input, result, 0, 124 + craftingOutputOffsetX, 35 + craftingOutputOffsetY))
|
||||
|
||||
|
||||
for (i in 0..2) {
|
||||
for (j in 0..2) {
|
||||
addSlot(Slot(input, j + i * 3, 30 + craftingGridOffsetX + j * 18, 17 + craftingGridOffsetY + i * 18))
|
||||
}
|
||||
}
|
||||
for (i in 0..2) {
|
||||
for (j in 0..8) {
|
||||
addSlot(Slot(playerInventory, j + i * 9 + 9, 8 + playerInventoryOffsetX + j * 18, 84 + playerInventoryOffsetY+ i * 18))
|
||||
}
|
||||
}
|
||||
for (i in 0..8) {
|
||||
addSlot(Slot(playerInventory, i, 8 + playerInventoryOffsetX + i * 18, 142 + playerInventoryOffsetY))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onContentChanged(inventory: Inventory) {
|
||||
context.run { world, pos -> updateResult(this@CookingPotScreenHandler, world, player, input, result) }
|
||||
}
|
||||
|
||||
override fun populateRecipeFinder(finder: RecipeMatcher) {
|
||||
input.provideRecipeInputs(finder)
|
||||
}
|
||||
|
||||
override fun clearCraftingSlots() {
|
||||
input.clear()
|
||||
result.clear()
|
||||
}
|
||||
|
||||
override fun matches(recipe: Recipe<in RecipeInputInventory>): Boolean {
|
||||
return recipe.matches(input, player.world)
|
||||
}
|
||||
|
||||
override fun onClosed(player: PlayerEntity) {
|
||||
super.onClosed(player)
|
||||
context.run { world, pos -> dropInventory(player, input) }
|
||||
}
|
||||
|
||||
override fun canUse(player: PlayerEntity): Boolean {
|
||||
return canUse(context, player, CobblemonBlocks.COOKING_POT)
|
||||
}
|
||||
|
||||
override fun quickMove(player: PlayerEntity, slot: Int): ItemStack {
|
||||
var itemStack = ItemStack.EMPTY
|
||||
val slot2 = slots[slot] as Slot
|
||||
if (slot2.hasStack()) {
|
||||
val itemStack2 = slot2.stack
|
||||
itemStack = itemStack2.copy()
|
||||
if (slot == 0) {
|
||||
context.run { world, pos -> itemStack2.item.onCraft(itemStack2, world, player) }
|
||||
if (!insertItem(itemStack2, 10, 46, true)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
slot2.onQuickTransfer(itemStack2, itemStack)
|
||||
} else if (slot in 10 until 46) {
|
||||
if (!insertItem(itemStack2, 1, 10, false) && (slot < 37 && !insertItem(itemStack2, 37, 46, false) || !insertItem(itemStack2, 10, 37, false))) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
} else if (!insertItem(itemStack2, 10, 46, false)) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
if (itemStack2.isEmpty) {
|
||||
slot2.stack = ItemStack.EMPTY
|
||||
} else {
|
||||
slot2.markDirty()
|
||||
}
|
||||
if (itemStack2.count == itemStack.count) {
|
||||
return ItemStack.EMPTY
|
||||
}
|
||||
slot2.onTakeItem(player, itemStack2)
|
||||
if (slot == 0) {
|
||||
player.dropItem(itemStack2, false)
|
||||
}
|
||||
}
|
||||
return itemStack
|
||||
}
|
||||
|
||||
override fun canInsertIntoSlot(stack: ItemStack, slot: Slot): Boolean {
|
||||
return slot.inventory !== result && super.canInsertIntoSlot(stack, slot)
|
||||
}
|
||||
|
||||
override fun getCraftingResultSlotIndex(): Int {
|
||||
return RESULT_ID
|
||||
}
|
||||
|
||||
override fun getCraftingWidth(): Int {
|
||||
return input.width
|
||||
}
|
||||
|
||||
override fun getCraftingHeight(): Int {
|
||||
return input.height
|
||||
}
|
||||
|
||||
override fun getCraftingSlotCount(): Int {
|
||||
return 10
|
||||
}
|
||||
|
||||
override fun getCategory(): RecipeBookCategory {
|
||||
return RecipeBookCategory.CRAFTING
|
||||
}
|
||||
|
||||
override fun canInsertIntoSlot(index: Int): Boolean {
|
||||
return index != craftingResultSlotIndex
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.google.common.collect.HashBasedTable
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import com.google.common.collect.Lists
|
||||
import com.google.common.collect.Maps
|
||||
import com.google.common.collect.Table
|
||||
import com.mojang.logging.LogUtils
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeResultCollection
|
||||
import net.minecraft.recipe.AbstractCookingRecipe
|
||||
import net.minecraft.recipe.CraftingRecipe
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.recipe.RecipeType
|
||||
import net.minecraft.recipe.book.CookingRecipeCategory
|
||||
import net.minecraft.recipe.book.CraftingRecipeCategory
|
||||
import net.minecraft.recipe.book.RecipeBook
|
||||
import net.minecraft.registry.DynamicRegistryManager
|
||||
import net.minecraft.registry.Registries
|
||||
import org.slf4j.Logger
|
||||
import java.util.*
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class ClientCookBook : RecipeBook() {
|
||||
private val logger: Logger = LogUtils.getLogger()
|
||||
private var resultsByGroup: Map<CookBookGroup, List<RecipeResultCollection>> = ImmutableMap.of()
|
||||
private var orderedResults: List<RecipeResultCollection> = ImmutableList.of()
|
||||
|
||||
fun reload(recipes: Iterable<Recipe<*>>, registryManager: DynamicRegistryManager) {
|
||||
val groupedMap = toGroupedMap(recipes)
|
||||
val map2 = Maps.newHashMap<CookBookGroup, List<RecipeResultCollection>>()
|
||||
val builder = ImmutableList.builder<RecipeResultCollection>()
|
||||
groupedMap.forEach { (recipeBookGroup, list) ->
|
||||
val var10002 = list.stream().map { recipes ->
|
||||
RecipeResultCollection(registryManager, recipes)
|
||||
}
|
||||
map2[recipeBookGroup] = var10002.peek { builder.add(it) }.collect(ImmutableList.toImmutableList())
|
||||
}
|
||||
CookBookGroup.SEARCH_MAP.forEach { (group, searchGroups) ->
|
||||
map2[group] = searchGroups.stream().flatMap { searchGroup ->
|
||||
map2.getOrDefault(searchGroup, ImmutableList.of()).stream()
|
||||
}.collect(ImmutableList.toImmutableList())
|
||||
}
|
||||
resultsByGroup = ImmutableMap.copyOf(map2)
|
||||
orderedResults = builder.build()
|
||||
}
|
||||
|
||||
private fun toGroupedMap(recipes: Iterable<Recipe<*>>): Map<CookBookGroup, List<List<Recipe<*>>>> {
|
||||
val map = Maps.newHashMap<CookBookGroup, MutableList<List<Recipe<*>>>>()
|
||||
val table: Table<CookBookGroup, String, MutableList<Recipe<*>>> = HashBasedTable.create()
|
||||
for (recipe in recipes) {
|
||||
if (!recipe.isIgnoredInRecipeBook && !recipe.isEmpty) {
|
||||
val recipeBookGroup = getGroupForRecipe(recipe)
|
||||
val group = recipe.group
|
||||
if (group.isEmpty()) {
|
||||
map.computeIfAbsent(recipeBookGroup) { Lists.newArrayList() }.add(ImmutableList.of(recipe))
|
||||
} else {
|
||||
val list = table.get(recipeBookGroup, group) ?: Lists.newArrayList<Recipe<*>>().also { newList ->
|
||||
table.put(recipeBookGroup, group, newList)
|
||||
map.computeIfAbsent(recipeBookGroup) { Lists.newArrayList() }.add(newList)
|
||||
}
|
||||
list.add(recipe)
|
||||
}
|
||||
}
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
private fun getGroupForRecipe(recipe: Recipe<*>): CookBookGroup {
|
||||
return when {
|
||||
recipe is CraftingRecipe -> {
|
||||
when (recipe.category) {
|
||||
CraftingRecipeCategory.BUILDING -> CookBookGroup.CRAFTING_BUILDING_BLOCKS
|
||||
CraftingRecipeCategory.EQUIPMENT -> CookBookGroup.CRAFTING_EQUIPMENT
|
||||
CraftingRecipeCategory.REDSTONE -> CookBookGroup.CRAFTING_REDSTONE
|
||||
CraftingRecipeCategory.MISC -> CookBookGroup.CRAFTING_MISC
|
||||
else -> throw IncompatibleClassChangeError()
|
||||
}
|
||||
}
|
||||
recipe is AbstractCookingRecipe -> {
|
||||
val cookingRecipeCategory = recipe.category
|
||||
when (recipe.type) {
|
||||
RecipeType.SMELTING -> {
|
||||
when (cookingRecipeCategory) {
|
||||
CookingRecipeCategory.BLOCKS -> CookBookGroup.FURNACE_BLOCKS
|
||||
CookingRecipeCategory.FOOD -> CookBookGroup.FURNACE_FOOD
|
||||
CookingRecipeCategory.MISC -> CookBookGroup.FURNACE_MISC
|
||||
else -> throw IncompatibleClassChangeError()
|
||||
}
|
||||
}
|
||||
RecipeType.BLASTING -> {
|
||||
if (cookingRecipeCategory == CookingRecipeCategory.BLOCKS) CookBookGroup.BLAST_FURNACE_BLOCKS else CookBookGroup.BLAST_FURNACE_MISC
|
||||
}
|
||||
RecipeType.SMOKING -> CookBookGroup.SMOKER_FOOD
|
||||
RecipeType.CAMPFIRE_COOKING -> CookBookGroup.CAMPFIRE
|
||||
else -> {
|
||||
logger.warn("Unknown recipe category: {}/{}", LogUtils.defer { Registries.RECIPE_TYPE.getId(recipe.type) }, LogUtils.defer { recipe.id })
|
||||
CookBookGroup.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
recipe.type == RecipeType.STONECUTTING -> CookBookGroup.STONECUTTER
|
||||
recipe.type == RecipeType.SMITHING -> CookBookGroup.SMITHING
|
||||
else -> {
|
||||
logger.warn("Unknown recipe category: {}/{}", LogUtils.defer { Registries.RECIPE_TYPE.getId(recipe.type) }, LogUtils.defer { recipe.id })
|
||||
CookBookGroup.UNKNOWN
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getOrderedResults(): List<RecipeResultCollection> {
|
||||
return orderedResults
|
||||
}
|
||||
|
||||
fun getResultsForGroup(category: CookBookGroup): List<RecipeResultCollection> {
|
||||
return resultsByGroup.getOrDefault(category, Collections.emptyList())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.google.common.collect.Sets
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.recipe.book.RecipeBook
|
||||
import net.minecraft.recipe.book.RecipeBookCategory
|
||||
import net.minecraft.recipe.book.RecipeBookOptions
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
import net.minecraft.util.Identifier
|
||||
import org.jetbrains.annotations.Nullable
|
||||
|
||||
class CookBook : RecipeBook() {
|
||||
protected val recipes: MutableSet<Identifier> = Sets.newHashSet()
|
||||
protected val toBeDisplayed: MutableSet<Identifier> = Sets.newHashSet()
|
||||
private val options = RecipeBookOptions()
|
||||
|
||||
fun copyFrom(book: CookBook) {
|
||||
recipes.clear()
|
||||
toBeDisplayed.clear()
|
||||
options.copyFrom(book.options)
|
||||
recipes.addAll(book.recipes)
|
||||
toBeDisplayed.addAll(book.toBeDisplayed)
|
||||
}
|
||||
|
||||
override fun add(recipe: Recipe<*>) {
|
||||
if (!recipe.isIgnoredInRecipeBook) {
|
||||
add(recipe.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun add(id: Identifier) {
|
||||
recipes.add(id)
|
||||
}
|
||||
|
||||
override fun contains(@Nullable recipe: Recipe<*>?): Boolean {
|
||||
return recipe?.let { recipes.contains(it.id) } ?: false
|
||||
}
|
||||
|
||||
override fun contains(id: Identifier): Boolean {
|
||||
return recipes.contains(id)
|
||||
}
|
||||
|
||||
override fun remove(recipe: Recipe<*>) {
|
||||
remove(recipe.id)
|
||||
}
|
||||
|
||||
override fun remove(id: Identifier) {
|
||||
recipes.remove(id)
|
||||
toBeDisplayed.remove(id)
|
||||
}
|
||||
|
||||
override fun shouldDisplay(recipe: Recipe<*>): Boolean {
|
||||
return toBeDisplayed.contains(recipe.id)
|
||||
}
|
||||
|
||||
override fun onRecipeDisplayed(recipe: Recipe<*>) {
|
||||
toBeDisplayed.remove(recipe.id)
|
||||
}
|
||||
|
||||
override fun display(recipe: Recipe<*>) {
|
||||
display(recipe.id)
|
||||
}
|
||||
|
||||
override fun display(id: Identifier) {
|
||||
toBeDisplayed.add(id)
|
||||
}
|
||||
|
||||
override fun isGuiOpen(category: RecipeBookCategory): Boolean {
|
||||
return options.isGuiOpen(category)
|
||||
}
|
||||
|
||||
override fun setGuiOpen(category: RecipeBookCategory, open: Boolean) {
|
||||
options.setGuiOpen(category, open)
|
||||
}
|
||||
|
||||
override fun isFilteringCraftable(handler: AbstractRecipeScreenHandler<*>): Boolean {
|
||||
return isFilteringCraftable(handler.category)
|
||||
}
|
||||
|
||||
override fun isFilteringCraftable(category: RecipeBookCategory): Boolean {
|
||||
return options.isFilteringCraftable(category)
|
||||
}
|
||||
|
||||
override fun setFilteringCraftable(category: RecipeBookCategory, filteringCraftable: Boolean) {
|
||||
options.setFilteringCraftable(category, filteringCraftable)
|
||||
}
|
||||
|
||||
override fun setOptions(options: RecipeBookOptions) {
|
||||
this.options.copyFrom(options)
|
||||
}
|
||||
|
||||
override fun getOptions(): RecipeBookOptions {
|
||||
return options.copy()
|
||||
}
|
||||
|
||||
override fun setCategoryOptions(category: RecipeBookCategory, guiOpen: Boolean, filteringCraftable: Boolean) {
|
||||
options.setGuiOpen(category, guiOpen)
|
||||
options.setFilteringCraftable(category, filteringCraftable)
|
||||
}
|
||||
}
|
||||
+136
@@ -0,0 +1,136 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.google.common.collect.Lists
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
|
||||
import net.minecraft.client.gui.screen.narration.NarrationPart
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookResults
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeResultCollection
|
||||
import net.minecraft.client.gui.tooltip.Tooltip
|
||||
import net.minecraft.client.gui.widget.ClickableWidget
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.recipe.book.RecipeBook
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
import net.minecraft.screen.ScreenTexts
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
import net.minecraft.util.math.MathHelper
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CookBookAnimatedResultButton : ClickableWidget(0, 0, 25, 25, ScreenTexts.EMPTY) {
|
||||
val BACKGROUND_TEXTURE = Identifier("textures/gui/recipe_book.png")
|
||||
val field_32414 = 15.0f
|
||||
val field_32415 = 25
|
||||
val field_32413 = 30
|
||||
val MORE_RECIPES_TEXT = Text.translatable("gui.recipebook.moreRecipes")
|
||||
lateinit var craftingScreenHandler: AbstractRecipeScreenHandler<*>
|
||||
lateinit var recipeBook: RecipeBook
|
||||
lateinit var resultCollection: RecipeResultCollection
|
||||
var time = 0f
|
||||
var bounce = 0f
|
||||
var currentResultIndex = 0
|
||||
|
||||
fun showResultCollection(resultCollection: RecipeResultCollection, results: CookBookResults) {
|
||||
this.resultCollection = resultCollection
|
||||
this.craftingScreenHandler = results.getClientValue().player?.currentScreenHandler as AbstractRecipeScreenHandler<*>
|
||||
this.recipeBook = results.getCookBookValue()
|
||||
val list = resultCollection.getResults(this.recipeBook.isFilteringCraftable(this.craftingScreenHandler))
|
||||
for (recipe in list) {
|
||||
if (this.recipeBook.shouldDisplay(recipe)) {
|
||||
results.onRecipesDisplayed(list)
|
||||
this.bounce = 15.0f
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getResultCollectionValue(): RecipeResultCollection {
|
||||
return this.resultCollection
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (!Screen.hasControlDown()) {
|
||||
this.time += delta
|
||||
}
|
||||
val minecraftClient = MinecraftClient.getInstance()
|
||||
var i = 29
|
||||
if (!this.resultCollection.hasCraftableRecipes()) {
|
||||
i += 25
|
||||
}
|
||||
var j = 206
|
||||
if (this.resultCollection.getResults(this.recipeBook.isFilteringCraftable(this.craftingScreenHandler)).size > 1) {
|
||||
j += 25
|
||||
}
|
||||
val bl = this.bounce > 0.0f
|
||||
if (bl) {
|
||||
val f = 1.0f + 0.1f * Math.sin((this.bounce / 15.0f * Math.PI).toDouble()).toFloat()
|
||||
context.matrices.push()
|
||||
context.matrices.translate((this.x + 8).toFloat(), (this.y + 12).toFloat(), 0.0f)
|
||||
context.matrices.scale(f, f, 1.0f)
|
||||
context.matrices.translate(-(this.x + 8).toFloat(), -(this.y + 12).toFloat(), 0.0f)
|
||||
this.bounce -= delta
|
||||
}
|
||||
context.drawTexture(BACKGROUND_TEXTURE, this.x, this.y, i, j, this.width, this.height)
|
||||
val list = this.results
|
||||
this.currentResultIndex = MathHelper.floor(this.time / 30.0f) % list.size
|
||||
val itemStack = list[this.currentResultIndex].getOutput(this.resultCollection.registryManager)
|
||||
var k = 4
|
||||
if (this.resultCollection.hasSingleOutput() && this.results.size > 1) {
|
||||
context.drawItem(itemStack, this.x + k + 1, this.y + k + 1, 0, 10)
|
||||
k--
|
||||
}
|
||||
context.drawItemWithoutEntity(itemStack, this.x + k, this.y + k)
|
||||
if (bl) {
|
||||
context.matrices.pop()
|
||||
}
|
||||
}
|
||||
|
||||
private val results: List<Recipe<*>>
|
||||
get() {
|
||||
val list = this.resultCollection.getRecipes(true)
|
||||
if (!this.recipeBook.isFilteringCraftable(this.craftingScreenHandler)) {
|
||||
list.addAll(this.resultCollection.getRecipes(false))
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
fun hasResults(): Boolean {
|
||||
return this.results.size == 1
|
||||
}
|
||||
|
||||
fun currentRecipe(): Recipe<*> {
|
||||
return this.results[this.currentResultIndex]
|
||||
}
|
||||
|
||||
fun getRecipeTooltip(): List<Text> {
|
||||
val itemStack = this.results[this.currentResultIndex].getOutput(this.resultCollection.registryManager)
|
||||
val list = Lists.newArrayList(Screen.getTooltipFromItem(MinecraftClient.getInstance(), itemStack))
|
||||
if (this.resultCollection.getResults(this.recipeBook.isFilteringCraftable(this.craftingScreenHandler)).size > 1) {
|
||||
list.add(MORE_RECIPES_TEXT)
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
override fun appendClickableNarrations(builder: NarrationMessageBuilder) {
|
||||
val itemStack = this.results[this.currentResultIndex].getOutput(this.resultCollection.registryManager)
|
||||
builder.put(NarrationPart.TITLE, Text.translatable("narration.recipe", itemStack.name))
|
||||
if (this.resultCollection.getResults(this.recipeBook.isFilteringCraftable(this.craftingScreenHandler)).size > 1) {
|
||||
builder.put(NarrationPart.USAGE, Text.translatable("narration.button.usage.hovered"), Text.translatable("narration.recipe.usage.more"))
|
||||
} else {
|
||||
builder.put(NarrationPart.USAGE, Text.translatable("narration.button.usage.hovered"))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getWidth(): Int {
|
||||
return 25
|
||||
}
|
||||
|
||||
override fun isValidClickButton(button: Int): Boolean {
|
||||
return button == 0 || button == 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.ImmutableMap
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.block.Blocks
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.item.Items
|
||||
import net.minecraft.recipe.book.RecipeBookCategory
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
enum class CookBookGroup(vararg entries: ItemStack) {
|
||||
CRAFTING_SEARCH(ItemStack(Items.COMPASS)),
|
||||
CRAFTING_BUILDING_BLOCKS(ItemStack(Blocks.BRICKS)),
|
||||
CRAFTING_REDSTONE(ItemStack(Items.REDSTONE)),
|
||||
CRAFTING_EQUIPMENT(ItemStack(Items.IRON_AXE), ItemStack(Items.GOLDEN_SWORD)),
|
||||
CRAFTING_MISC(ItemStack(Items.LAVA_BUCKET), ItemStack(Items.APPLE)),
|
||||
FURNACE_SEARCH(ItemStack(Items.COMPASS)),
|
||||
FURNACE_FOOD(ItemStack(Items.PORKCHOP)),
|
||||
FURNACE_BLOCKS(ItemStack(Blocks.STONE)),
|
||||
FURNACE_MISC(ItemStack(Items.LAVA_BUCKET), ItemStack(Items.EMERALD)),
|
||||
BLAST_FURNACE_SEARCH(ItemStack(Items.COMPASS)),
|
||||
BLAST_FURNACE_BLOCKS(ItemStack(Blocks.REDSTONE_ORE)),
|
||||
BLAST_FURNACE_MISC(ItemStack(Items.IRON_SHOVEL), ItemStack(Items.GOLDEN_LEGGINGS)),
|
||||
SMOKER_SEARCH(ItemStack(Items.COMPASS)),
|
||||
SMOKER_FOOD(ItemStack(Items.PORKCHOP)),
|
||||
STONECUTTER(ItemStack(Items.CHISELED_STONE_BRICKS)),
|
||||
SMITHING(ItemStack(Items.NETHERITE_CHESTPLATE)),
|
||||
CAMPFIRE(ItemStack(Items.PORKCHOP)),
|
||||
UNKNOWN(ItemStack(Items.BARRIER));
|
||||
|
||||
val icons: List<ItemStack> = ImmutableList.copyOf(entries)
|
||||
|
||||
companion object {
|
||||
val SMOKER: List<CookBookGroup> = ImmutableList.of(SMOKER_SEARCH, SMOKER_FOOD)
|
||||
val BLAST_FURNACE: List<CookBookGroup> = ImmutableList.of(BLAST_FURNACE_SEARCH, BLAST_FURNACE_BLOCKS, BLAST_FURNACE_MISC)
|
||||
val FURNACE: List<CookBookGroup> = ImmutableList.of(FURNACE_SEARCH, FURNACE_FOOD, FURNACE_BLOCKS, FURNACE_MISC)
|
||||
val CRAFTING: List<CookBookGroup> = ImmutableList.of(CRAFTING_SEARCH, CRAFTING_EQUIPMENT, CRAFTING_BUILDING_BLOCKS, CRAFTING_MISC, CRAFTING_REDSTONE)
|
||||
val SEARCH_MAP: Map<CookBookGroup, List<CookBookGroup>> = ImmutableMap.of(
|
||||
CRAFTING_SEARCH, ImmutableList.of(CRAFTING_EQUIPMENT, CRAFTING_BUILDING_BLOCKS, CRAFTING_MISC, CRAFTING_REDSTONE),
|
||||
FURNACE_SEARCH, ImmutableList.of(FURNACE_FOOD, FURNACE_BLOCKS, FURNACE_MISC),
|
||||
BLAST_FURNACE_SEARCH, ImmutableList.of(BLAST_FURNACE_BLOCKS, BLAST_FURNACE_MISC),
|
||||
SMOKER_SEARCH, ImmutableList.of(SMOKER_FOOD)
|
||||
)
|
||||
|
||||
fun getGroups(category: RecipeBookCategory): List<CookBookGroup> {
|
||||
return when (category) {
|
||||
RecipeBookCategory.CRAFTING -> CRAFTING
|
||||
RecipeBookCategory.FURNACE -> FURNACE
|
||||
RecipeBookCategory.BLAST_FURNACE -> BLAST_FURNACE
|
||||
RecipeBookCategory.SMOKER -> SMOKER
|
||||
else -> throw IncompatibleClassChangeError()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getIconsValue(): List<ItemStack> {
|
||||
return this.icons
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package com.cobblemon.mod.common.gui.cooking;
|
||||
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
interface CookBookProvider {
|
||||
fun refreshCookBook()
|
||||
|
||||
fun getCookBookWidget(): CookBookWidget
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.google.common.collect.ImmutableList
|
||||
import com.google.common.collect.Lists
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.recipebook.AnimatedResultButton
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeAlternativesWidget
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeDisplayListener
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeResultCollection
|
||||
import net.minecraft.client.gui.widget.ClickableWidget
|
||||
import net.minecraft.client.gui.widget.ToggleButtonWidget
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.recipe.book.RecipeBook
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import java.util.function.Consumer
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CookBookResults {
|
||||
val resultButtons = Lists.newArrayListWithCapacity<CookBookAnimatedResultButton>(20)
|
||||
@Nullable
|
||||
var hoveredResultButton: CookBookAnimatedResultButton? = null
|
||||
val alternatesWidget = RecipeAlternativesWidget()
|
||||
lateinit var client: MinecraftClient
|
||||
val recipeDisplayListeners = Lists.newArrayList<RecipeDisplayListener>()
|
||||
var resultCollections = ImmutableList.of<RecipeResultCollection>()
|
||||
lateinit var nextPageButton: ToggleButtonWidget
|
||||
lateinit var prevPageButton: ToggleButtonWidget
|
||||
var pageCount = 0
|
||||
var currentPage = 0
|
||||
lateinit var recipeBook: RecipeBook
|
||||
@Nullable
|
||||
var lastClickedRecipe: Recipe<*>? = null
|
||||
@Nullable
|
||||
var resultCollection: RecipeResultCollection? = null
|
||||
|
||||
init {
|
||||
for (i in 0 until 20) {
|
||||
resultButtons.add(CookBookAnimatedResultButton())
|
||||
/*resultButtons.add(CookBookAnimatedResultButton().apply {
|
||||
// Initialize resultCollection with a default or dummy value if possible
|
||||
this.resultCollection = RecipeResultCollection()
|
||||
})*/
|
||||
}
|
||||
}
|
||||
|
||||
fun initialize(client: MinecraftClient, parentLeft: Int, parentTop: Int) {
|
||||
this.client = client
|
||||
this.recipeBook = client.player?.recipeBook ?: return
|
||||
|
||||
for (i in resultButtons.indices) {
|
||||
resultButtons[i].setPosition(parentLeft + 11 + 25 * (i % 5), parentTop + 31 + 25 * (i / 5))
|
||||
}
|
||||
|
||||
nextPageButton = ToggleButtonWidget(parentLeft + 93, parentTop + 137, 12, 17, false)
|
||||
nextPageButton.setTextureUV(1, 208, 13, 18, CookBookWidget.TEXTURE)
|
||||
prevPageButton = ToggleButtonWidget(parentLeft + 38, parentTop + 137, 12, 17, true)
|
||||
prevPageButton.setTextureUV(1, 208, 13, 18, CookBookWidget.TEXTURE)
|
||||
}
|
||||
|
||||
fun setGui(widget: CookBookWidget) {
|
||||
recipeDisplayListeners.remove(widget)
|
||||
recipeDisplayListeners.add(widget)
|
||||
}
|
||||
|
||||
fun setResults(resultCollections: List<RecipeResultCollection>, resetCurrentPage: Boolean) {
|
||||
this.resultCollections = ImmutableList.copyOf(resultCollections)
|
||||
this.pageCount = Math.ceil(resultCollections.size / 20.0).toInt()
|
||||
if (this.pageCount <= this.currentPage || resetCurrentPage) {
|
||||
this.currentPage = 0
|
||||
}
|
||||
refreshResultButtons()
|
||||
}
|
||||
|
||||
fun refreshResultButtons() {
|
||||
val i = 20 * this.currentPage
|
||||
for (j in resultButtons.indices) {
|
||||
val animatedResultButton = resultButtons[j]
|
||||
if (i + j < resultCollections.size) {
|
||||
val recipeResultCollection = resultCollections[i + j]
|
||||
animatedResultButton.showResultCollection(recipeResultCollection, this)
|
||||
animatedResultButton.visible = true
|
||||
} else {
|
||||
animatedResultButton.visible = false
|
||||
}
|
||||
}
|
||||
hideShowPageButtons()
|
||||
}
|
||||
|
||||
fun hideShowPageButtons() {
|
||||
nextPageButton.visible = pageCount > 1 && currentPage < pageCount - 1
|
||||
prevPageButton.visible = pageCount > 1 && currentPage > 0
|
||||
}
|
||||
|
||||
fun draw(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (pageCount > 1) {
|
||||
val string = "${currentPage + 1}/$pageCount"
|
||||
val i = client.textRenderer.getWidth(string)
|
||||
context.drawText(client.textRenderer, string, x - i / 2 + 73, y + 141, -1, false)
|
||||
}
|
||||
hoveredResultButton = null
|
||||
for (animatedResultButton in resultButtons) {
|
||||
animatedResultButton.render(context, mouseX, mouseY, delta)
|
||||
if (animatedResultButton.visible && animatedResultButton.isSelected()) {
|
||||
hoveredResultButton = animatedResultButton
|
||||
}
|
||||
}
|
||||
prevPageButton.render(context, mouseX, mouseY, delta)
|
||||
nextPageButton.render(context, mouseX, mouseY, delta)
|
||||
alternatesWidget.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
fun drawTooltip(context: DrawContext, x: Int, y: Int) {
|
||||
if (client.currentScreen != null && hoveredResultButton != null && !alternatesWidget.isVisible()) {
|
||||
context.drawTooltip(client.textRenderer, hoveredResultButton!!.getRecipeTooltip(), x, y)
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
fun getLastClickedRecipeValue(): Recipe<*>? {
|
||||
return lastClickedRecipe
|
||||
}
|
||||
|
||||
@Nullable
|
||||
fun getLastClickedResults(): RecipeResultCollection? {
|
||||
return resultCollection
|
||||
}
|
||||
|
||||
fun hideAlternates() {
|
||||
alternatesWidget.isVisible = false
|
||||
}
|
||||
|
||||
fun mouseClicked(mouseX: Double, mouseY: Double, button: Int, areaLeft: Int, areaTop: Int, areaWidth: Int, areaHeight: Int): Boolean {
|
||||
lastClickedRecipe = null
|
||||
resultCollection = null
|
||||
if (alternatesWidget.isVisible()) {
|
||||
if (alternatesWidget.mouseClicked(mouseX, mouseY, button)) {
|
||||
lastClickedRecipe = alternatesWidget.getLastClickedRecipe()
|
||||
resultCollection = alternatesWidget.getResults()
|
||||
} else {
|
||||
alternatesWidget.isVisible = false
|
||||
}
|
||||
return true
|
||||
} else if (nextPageButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
currentPage++
|
||||
refreshResultButtons()
|
||||
return true
|
||||
} else if (prevPageButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
currentPage--
|
||||
refreshResultButtons()
|
||||
return true
|
||||
} else {
|
||||
for (animatedResultButton in resultButtons) {
|
||||
if (animatedResultButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
if (button == 0) {
|
||||
lastClickedRecipe = animatedResultButton.currentRecipe()
|
||||
resultCollection = animatedResultButton.getResultCollectionValue()
|
||||
} else if (button == 1 && !alternatesWidget.isVisible() && !animatedResultButton.hasResults()) {
|
||||
alternatesWidget.showAlternativesForResult(client, animatedResultButton.getResultCollectionValue(), animatedResultButton.x, animatedResultButton.y, areaLeft + areaWidth / 2, areaTop + 13 + areaHeight / 2, animatedResultButton.width.toFloat())
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun onRecipesDisplayed(recipes: List<Recipe<*>>) {
|
||||
for (recipeDisplayListener in recipeDisplayListeners) {
|
||||
recipeDisplayListener.onRecipesDisplayed(recipes)
|
||||
}
|
||||
}
|
||||
|
||||
fun getClientValue(): MinecraftClient {
|
||||
return client
|
||||
}
|
||||
|
||||
fun getCookBookValue(): RecipeBook {
|
||||
return recipeBook
|
||||
}
|
||||
|
||||
fun forEachButton(consumer: Consumer<ClickableWidget>) {
|
||||
consumer.accept(nextPageButton)
|
||||
consumer.accept(prevPageButton)
|
||||
resultButtons.forEach(consumer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val field_32411 = 20
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,499 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.google.common.collect.Lists
|
||||
import it.unimi.dsi.fastutil.objects.ObjectLinkedOpenHashSet
|
||||
import it.unimi.dsi.fastutil.objects.ObjectSet
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.font.TextRenderer
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.Drawable
|
||||
import net.minecraft.client.gui.Element
|
||||
import net.minecraft.client.gui.Selectable
|
||||
import net.minecraft.client.gui.Selectable.SelectionType
|
||||
import net.minecraft.client.gui.screen.Screen
|
||||
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookGhostSlots
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeDisplayListener
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeGroupButtonWidget
|
||||
import net.minecraft.client.gui.tooltip.Tooltip
|
||||
import net.minecraft.client.gui.widget.TextFieldWidget
|
||||
import net.minecraft.client.gui.widget.ToggleButtonWidget
|
||||
import net.minecraft.client.recipebook.ClientRecipeBook
|
||||
import net.minecraft.client.recipebook.RecipeBookGroup
|
||||
import net.minecraft.client.resource.language.LanguageDefinition
|
||||
import net.minecraft.client.resource.language.LanguageManager
|
||||
import net.minecraft.client.search.SearchManager
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.network.packet.c2s.play.RecipeCategoryOptionsC2SPacket
|
||||
import net.minecraft.recipe.Ingredient
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.recipe.RecipeGridAligner
|
||||
import net.minecraft.recipe.RecipeMatcher
|
||||
import net.minecraft.recipe.book.RecipeBookCategory
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Formatting
|
||||
import net.minecraft.util.Identifier
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import java.util.*
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CookBookWidget : RecipeGridAligner<Ingredient>, Drawable, Element, Selectable, RecipeDisplayListener {
|
||||
//val SEARCH_HINT_TEXT: Text
|
||||
val field_32408 = 147
|
||||
val field_32409 = 166
|
||||
val field_32410 = 86
|
||||
//val TOGGLE_CRAFTABLE_RECIPES_TEXT: Text
|
||||
//val TOGGLE_ALL_RECIPES_TEXT: Text
|
||||
var leftOffset = 0
|
||||
var parentWidth = 0
|
||||
var parentHeight = 0
|
||||
val ghostSlots = RecipeBookGhostSlots()
|
||||
val tabButtons: ArrayList<CookingRecipeGroupButtonWidget> = Lists.newArrayList<CookingRecipeGroupButtonWidget>()
|
||||
@Nullable
|
||||
var currentTab: CookingRecipeGroupButtonWidget? = null
|
||||
lateinit var toggleCraftableButton: ToggleButtonWidget
|
||||
lateinit var craftingScreenHandler: AbstractRecipeScreenHandler<*>
|
||||
lateinit var client: MinecraftClient
|
||||
@Nullable
|
||||
var searchField: TextFieldWidget? = null
|
||||
var searchText = ""
|
||||
lateinit var recipeBook: ClientRecipeBook
|
||||
val recipesArea = CookBookResults()
|
||||
val recipeFinder = RecipeMatcher()
|
||||
var cachedInvChangeCount = 0
|
||||
var searching = false
|
||||
var open = false
|
||||
var narrow = false
|
||||
|
||||
fun initialize(parentWidth: Int, parentHeight: Int, client: MinecraftClient?, narrow: Boolean, craftingScreenHandler: AbstractRecipeScreenHandler<*>) {
|
||||
if (client != null) {
|
||||
this.client = client
|
||||
}
|
||||
this.parentWidth = parentWidth
|
||||
this.parentHeight = parentHeight
|
||||
this.craftingScreenHandler = craftingScreenHandler
|
||||
this.narrow = narrow
|
||||
client?.player?.currentScreenHandler = craftingScreenHandler
|
||||
this.recipeBook = client?.player?.recipeBook ?: return
|
||||
this.cachedInvChangeCount = client?.player?.inventory?.changeCount ?: 0
|
||||
this.open = isGuiOpen()
|
||||
if (this.open) {
|
||||
reset()
|
||||
}
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
leftOffset = if (narrow) 0 else 86
|
||||
val i = (parentWidth - 147) / 2 - leftOffset
|
||||
val j = (parentHeight - 166) / 2
|
||||
recipeFinder.clear()
|
||||
client.player?.inventory?.populateRecipeFinder(recipeFinder)
|
||||
craftingScreenHandler.populateRecipeFinder(recipeFinder)
|
||||
val string = searchField?.text ?: ""
|
||||
val var10003 = client.textRenderer
|
||||
val var10004 = i + 26
|
||||
val var10005 = j + 14
|
||||
Objects.requireNonNull(client.textRenderer)
|
||||
searchField = TextFieldWidget(var10003, var10004, var10005, 79, 9 + 3, Text.translatable("itemGroup.search"))
|
||||
searchField?.setMaxLength(50)
|
||||
searchField?.isVisible = true
|
||||
searchField?.setEditableColor(16777215)
|
||||
searchField?.text = string
|
||||
searchField?.setPlaceholder(SEARCH_HINT_TEXT)
|
||||
recipesArea.initialize(client, i, j)
|
||||
recipesArea.setGui(this)
|
||||
toggleCraftableButton = ToggleButtonWidget(i + 110, j + 12, 26, 16, recipeBook.isFilteringCraftable(craftingScreenHandler))
|
||||
updateTooltip()
|
||||
setBookButtonTexture()
|
||||
tabButtons.clear()
|
||||
val var4 = RecipeBookGroup.getGroups(craftingScreenHandler.category).iterator()
|
||||
while (var4.hasNext()) {
|
||||
val recipeBookGroup = var4.next() as RecipeBookGroup
|
||||
tabButtons.add(CookingRecipeGroupButtonWidget(recipeBookGroup))
|
||||
}
|
||||
if (currentTab != null) {
|
||||
currentTab = tabButtons.stream().filter { button ->
|
||||
button.category == currentTab?.getCategoryValue()
|
||||
}.findFirst().orElse(null)
|
||||
}
|
||||
if (currentTab == null) {
|
||||
currentTab = tabButtons[0]
|
||||
}
|
||||
currentTab?.setToggled(true)
|
||||
refreshResults(false)
|
||||
refreshTabButtons()
|
||||
}
|
||||
|
||||
private fun updateTooltip() {
|
||||
toggleCraftableButton.setTooltip(if (toggleCraftableButton.isToggled) Tooltip.of(getToggleCraftableButtonText()) else Tooltip.of(TOGGLE_ALL_RECIPES_TEXT))
|
||||
}
|
||||
|
||||
fun setBookButtonTexture() {
|
||||
toggleCraftableButton.setTextureUV(152, 41, 28, 18, TEXTURE)
|
||||
}
|
||||
|
||||
fun findLeftEdge(width: Int, backgroundWidth: Int): Int {
|
||||
return if (isOpen() && !narrow) {
|
||||
177 + (width - backgroundWidth - 200) / 2
|
||||
} else {
|
||||
(width - backgroundWidth) / 2
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleOpen() {
|
||||
setOpenValue(!isOpen())
|
||||
}
|
||||
|
||||
fun isOpen(): Boolean {
|
||||
return open
|
||||
}
|
||||
|
||||
fun isGuiOpen(): Boolean {
|
||||
return recipeBook.isGuiOpen(craftingScreenHandler.category)
|
||||
}
|
||||
|
||||
fun setOpenValue(opened: Boolean) {
|
||||
if (opened) {
|
||||
reset()
|
||||
}
|
||||
open = opened
|
||||
recipeBook.setGuiOpen(craftingScreenHandler.category, opened)
|
||||
if (!opened) {
|
||||
recipesArea.hideAlternates()
|
||||
}
|
||||
sendBookDataPacket()
|
||||
}
|
||||
|
||||
fun slotClicked(@Nullable slot: Slot?) {
|
||||
if (slot != null && slot.id < craftingScreenHandler.craftingSlotCount) {
|
||||
ghostSlots.reset()
|
||||
if (isOpen()) {
|
||||
refreshInputs()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshResults(resetCurrentPage: Boolean) {
|
||||
val list = recipeBook.getResultsForGroup(currentTab?.category ?: return)
|
||||
list.forEach { resultCollection ->
|
||||
resultCollection.computeCraftables(recipeFinder, craftingScreenHandler.craftingWidth, craftingScreenHandler.craftingHeight, recipeBook)
|
||||
}
|
||||
val list2 = Lists.newArrayList(list)
|
||||
list2.removeIf { !it.isInitialized }
|
||||
list2.removeIf { !it.hasFittingRecipes() }
|
||||
val string = searchField?.text ?: ""
|
||||
if (string.isNotEmpty()) {
|
||||
val objectSet = ObjectLinkedOpenHashSet(client.getSearchProvider(SearchManager.RECIPE_OUTPUT).findAll(string.lowercase(Locale.ROOT)))
|
||||
list2.removeIf { !objectSet.contains(it) }
|
||||
}
|
||||
if (recipeBook.isFilteringCraftable(craftingScreenHandler)) {
|
||||
list2.removeIf { !it.hasCraftableRecipes() }
|
||||
}
|
||||
recipesArea.setResults(list2, resetCurrentPage)
|
||||
}
|
||||
|
||||
fun refreshTabButtons() {
|
||||
val i = (parentWidth - 147) / 2 - leftOffset - 30
|
||||
val j = (parentHeight - 166) / 2 + 3
|
||||
var l = 0
|
||||
val var5 = tabButtons.iterator()
|
||||
while (true) {
|
||||
while (var5.hasNext()) {
|
||||
val recipeGroupButtonWidget = var5.next()
|
||||
val recipeBookGroup = recipeGroupButtonWidget.category
|
||||
if (recipeBookGroup != RecipeBookGroup.CRAFTING_SEARCH && recipeBookGroup != RecipeBookGroup.FURNACE_SEARCH) {
|
||||
if (recipeGroupButtonWidget.hasKnownRecipes(recipeBook)) {
|
||||
recipeGroupButtonWidget.setPosition(i, j + 27 * l++)
|
||||
recipeGroupButtonWidget.checkForNewRecipes(client)
|
||||
}
|
||||
} else {
|
||||
recipeGroupButtonWidget.visible = true
|
||||
recipeGroupButtonWidget.setPosition(i, j + 27 * l++)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
fun update() {
|
||||
val bl = isGuiOpen()
|
||||
if (isOpen() != bl) {
|
||||
setOpenValue(bl)
|
||||
}
|
||||
if (isOpen()) {
|
||||
if (cachedInvChangeCount != client.player?.inventory?.changeCount) {
|
||||
refreshInputs()
|
||||
cachedInvChangeCount = client.player?.inventory?.changeCount ?: 0
|
||||
}
|
||||
searchField?.tick()
|
||||
}
|
||||
}
|
||||
|
||||
fun refreshInputs() {
|
||||
recipeFinder.clear()
|
||||
client.player?.inventory?.populateRecipeFinder(recipeFinder)
|
||||
craftingScreenHandler.populateRecipeFinder(recipeFinder)
|
||||
refreshResults(false)
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (isOpen()) {
|
||||
context.matrices.push()
|
||||
context.matrices.translate(0.0F, 0.0F, 100.0F)
|
||||
val i = (parentWidth - 147) / 2 - leftOffset
|
||||
val j = (parentHeight - 166) / 2
|
||||
context.drawTexture(TEXTURE, i, j, 1, 1, 147, 166)
|
||||
searchField?.render(context, mouseX, mouseY, delta)
|
||||
for (recipeGroupButtonWidget in tabButtons) {
|
||||
recipeGroupButtonWidget.render(context, mouseX, mouseY, delta)
|
||||
}
|
||||
toggleCraftableButton.render(context, mouseX, mouseY, delta)
|
||||
recipesArea.draw(context, i, j, mouseX, mouseY, delta)
|
||||
context.matrices.pop()
|
||||
}
|
||||
}
|
||||
|
||||
fun drawTooltip(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
|
||||
if (isOpen()) {
|
||||
recipesArea.drawTooltip(context, mouseX, mouseY)
|
||||
drawGhostSlotTooltip(context, x, y, mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
fun getToggleCraftableButtonText(): Text {
|
||||
return TOGGLE_CRAFTABLE_RECIPES_TEXT
|
||||
}
|
||||
|
||||
fun drawGhostSlotTooltip(context: DrawContext, x: Int, y: Int, mouseX: Int, mouseY: Int) {
|
||||
var itemStack: ItemStack? = null
|
||||
for (i in 0 until ghostSlots.slotCount) {
|
||||
val ghostInputSlot = ghostSlots.getSlot(i)
|
||||
val j = ghostInputSlot.x + x
|
||||
val k = ghostInputSlot.y + y
|
||||
if (mouseX >= j && mouseY >= k && mouseX < j + 16 && mouseY < k + 16) {
|
||||
itemStack = ghostInputSlot.currentItemStack
|
||||
}
|
||||
}
|
||||
if (itemStack != null && client.currentScreen != null) {
|
||||
context.drawTooltip(client.textRenderer, Screen.getTooltipFromItem(client, itemStack), mouseX, mouseY)
|
||||
}
|
||||
}
|
||||
|
||||
fun drawGhostSlots(context: DrawContext, x: Int, y: Int, notInventory: Boolean, delta: Float) {
|
||||
ghostSlots.draw(context, client, x, y, notInventory, delta)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (isOpen() && !client.player?.isSpectator()!!) {
|
||||
if (recipesArea.mouseClicked(mouseX, mouseY, button, (parentWidth - 147) / 2 - leftOffset, (parentHeight - 166) / 2, 147, 166)) {
|
||||
val recipe = recipesArea.getLastClickedRecipeValue()
|
||||
val recipeResultCollection = recipesArea.getLastClickedResults()
|
||||
if (recipe != null && recipeResultCollection != null) {
|
||||
if (!recipeResultCollection.isCraftable(recipe) && ghostSlots.recipe == recipe) {
|
||||
return false
|
||||
}
|
||||
ghostSlots.reset()
|
||||
client.interactionManager?.clickRecipe(client.player?.currentScreenHandler?.syncId ?: 0, recipe, Screen.hasShiftDown())
|
||||
if (!isWide()) {
|
||||
setOpenValue(false)
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else if (searchField?.mouseClicked(mouseX, mouseY, button) == true) {
|
||||
searchField?.setFocused(true)
|
||||
return true
|
||||
} else {
|
||||
searchField?.setFocused(false)
|
||||
if (toggleCraftableButton.mouseClicked(mouseX, mouseY, button)) {
|
||||
val bl = toggleFilteringCraftable()
|
||||
toggleCraftableButton.setToggled(bl)
|
||||
updateTooltip()
|
||||
sendBookDataPacket()
|
||||
refreshResults(false)
|
||||
return true
|
||||
} else {
|
||||
for (recipeGroupButtonWidget in tabButtons) {
|
||||
if (recipeGroupButtonWidget.mouseClicked(mouseX, mouseY, button)) {
|
||||
if (currentTab != recipeGroupButtonWidget) {
|
||||
currentTab?.setToggled(false)
|
||||
currentTab = recipeGroupButtonWidget
|
||||
currentTab?.setToggled(true)
|
||||
refreshResults(true)
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun toggleFilteringCraftable(): Boolean {
|
||||
val recipeBookCategory = craftingScreenHandler.category
|
||||
val bl = !recipeBook.isFilteringCraftable(recipeBookCategory)
|
||||
recipeBook.setFilteringCraftable(recipeBookCategory, bl)
|
||||
return bl
|
||||
}
|
||||
|
||||
fun isClickOutsideBounds(mouseX: Double, mouseY: Double, x: Int, y: Int, backgroundWidth: Int, backgroundHeight: Int, button: Int): Boolean {
|
||||
return if (!isOpen()) {
|
||||
true
|
||||
} else {
|
||||
val bl = mouseX < x.toDouble() || mouseY < y.toDouble() || mouseX >= (x + backgroundWidth).toDouble() || mouseY >= (y + backgroundHeight).toDouble()
|
||||
val bl2 = (x - 147).toDouble() < mouseX && mouseX < x.toDouble() && y.toDouble() < mouseY && mouseY < (y + backgroundHeight).toDouble()
|
||||
bl && !bl2 && !currentTab?.isSelected()!!
|
||||
}
|
||||
}
|
||||
|
||||
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
searching = false
|
||||
if (isOpen() && !client.player?.isSpectator()!!) {
|
||||
return if (keyCode == 256 && !isWide()) {
|
||||
setOpenValue(false)
|
||||
true
|
||||
} else if (searchField?.keyPressed(keyCode, scanCode, modifiers) == true) {
|
||||
refreshSearchResults()
|
||||
true
|
||||
} else if (searchField?.isFocused == true && searchField?.isVisible == true && keyCode != 256) {
|
||||
true
|
||||
} else if (client.options.chatKey.matchesKey(keyCode, scanCode) && searchField?.isFocused == false) {
|
||||
searching = true
|
||||
searchField?.setFocused(true)
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun keyReleased(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
|
||||
searching = false
|
||||
return super.keyReleased(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
override fun charTyped(chr: Char, modifiers: Int): Boolean {
|
||||
return if (searching) {
|
||||
false
|
||||
} else if (isOpen() && !client.player?.isSpectator()!!) {
|
||||
if (searchField?.charTyped(chr, modifiers) == true) {
|
||||
refreshSearchResults()
|
||||
true
|
||||
} else {
|
||||
super.charTyped(chr, modifiers)
|
||||
}
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
override fun isMouseOver(mouseX: Double, mouseY: Double): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun setFocused(focused: Boolean) {}
|
||||
|
||||
override fun isFocused(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
fun refreshSearchResults() {
|
||||
val string = searchField?.text?.lowercase(Locale.ROOT) ?: ""
|
||||
triggerPirateSpeakEasterEgg(string)
|
||||
if (string != searchText) {
|
||||
refreshResults(false)
|
||||
searchText = string
|
||||
}
|
||||
}
|
||||
|
||||
fun triggerPirateSpeakEasterEgg(search: String) {
|
||||
if ("excitedze" == search) {
|
||||
val languageManager = client.languageManager
|
||||
val string = "en_pt"
|
||||
val languageDefinition = languageManager.getLanguage("en_pt")
|
||||
if (languageDefinition == null || languageManager.language == "en_pt") {
|
||||
return
|
||||
}
|
||||
languageManager.language = "en_pt"
|
||||
client.options.language = "en_pt"
|
||||
client.reloadResources()
|
||||
client.options.write()
|
||||
}
|
||||
}
|
||||
|
||||
fun isWide(): Boolean {
|
||||
return leftOffset == 86
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
refreshTabButtons()
|
||||
if (isOpen()) {
|
||||
refreshResults(false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRecipesDisplayed(recipes: List<Recipe<*>>) {
|
||||
for (recipe in recipes) {
|
||||
client.player?.onRecipeDisplayed(recipe)
|
||||
}
|
||||
}
|
||||
|
||||
fun showGhostRecipe(recipe: Recipe<*>, slots: List<Slot>) {
|
||||
val itemStack = recipe.getOutput(client.world?.registryManager)
|
||||
ghostSlots.setRecipe(recipe)
|
||||
ghostSlots.addSlot(Ingredient.ofStacks(itemStack), slots[0].x, slots[0].y)
|
||||
alignRecipeToGrid(craftingScreenHandler.craftingWidth, craftingScreenHandler.craftingHeight, craftingScreenHandler.craftingResultSlotIndex, recipe, recipe.ingredients.iterator(), 0)
|
||||
}
|
||||
|
||||
override fun acceptAlignedInput(inputs: Iterator<Ingredient>, slot: Int, amount: Int, gridX: Int, gridY: Int) {
|
||||
val ingredient = inputs.next()
|
||||
if (!ingredient.isEmpty) {
|
||||
val slot2 = craftingScreenHandler.slots[slot]
|
||||
ghostSlots.addSlot(ingredient, slot2.x, slot2.y)
|
||||
}
|
||||
}
|
||||
|
||||
fun sendBookDataPacket() {
|
||||
if (client.networkHandler != null) {
|
||||
val recipeBookCategory = craftingScreenHandler.category
|
||||
val bl = recipeBook.options.isGuiOpen(recipeBookCategory)
|
||||
val bl2 = recipeBook.options.isFilteringCraftable(recipeBookCategory)
|
||||
client.networkHandler?.sendPacket(RecipeCategoryOptionsC2SPacket(recipeBookCategory, bl, bl2))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getType(): SelectionType {
|
||||
return if (open) SelectionType.HOVERED else SelectionType.NONE
|
||||
}
|
||||
|
||||
override fun appendNarrations(builder: NarrationMessageBuilder) {
|
||||
val list = Lists.newArrayList<Selectable>()
|
||||
recipesArea.forEachButton { button ->
|
||||
if (button.isNarratable) {
|
||||
list.add(button)
|
||||
}
|
||||
}
|
||||
searchField?.let { list.add(it) } // not sure if this is the right way
|
||||
list.add(toggleCraftableButton)
|
||||
list.addAll(tabButtons)
|
||||
val selectedElementNarrationData = Screen.findSelectedElementData(list, null)
|
||||
selectedElementNarrationData?.selectable?.appendNarrations(builder.nextMessage())
|
||||
}
|
||||
|
||||
companion object {
|
||||
val TEXTURE = Identifier("textures/gui/recipe_book.png")
|
||||
val SEARCH_HINT_TEXT: Text = Text.translatable("gui.recipebook.search_hint").formatted(Formatting.ITALIC).formatted(Formatting.GRAY)
|
||||
val TOGGLE_CRAFTABLE_RECIPES_TEXT: Text = Text.translatable("gui.recipebook.toggleRecipes.craftable")
|
||||
val TOGGLE_ALL_RECIPES_TEXT: Text = Text.translatable("gui.recipebook.toggleRecipes.all")
|
||||
|
||||
}
|
||||
}
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.gui.CookingPotScreenHandler
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.ingame.HandledScreen
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookProvider
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget
|
||||
import net.minecraft.client.gui.widget.TexturedButtonWidget
|
||||
import net.minecraft.entity.player.PlayerInventory
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
import net.minecraft.screen.slot.Slot
|
||||
import net.minecraft.screen.slot.SlotActionType
|
||||
import net.minecraft.text.Text
|
||||
import net.minecraft.util.Identifier
|
||||
|
||||
class CookingPotHandledScreen(
|
||||
handler: CookingPotScreenHandler,
|
||||
inventory: PlayerInventory,
|
||||
title: Text?
|
||||
) : HandledScreen<CookingPotScreenHandler>(handler, inventory, title), CookBookProvider {
|
||||
|
||||
private val recipeBook = CookBookWidget()
|
||||
private var narrow = false
|
||||
|
||||
companion object {
|
||||
//private val TEXTURE = Identifier("textures/gui/container/crafting_table.png")
|
||||
val TEXTURE = cobblemonResource("textures/gui/cooking/cooking_pot.png")
|
||||
private val RECIPE_BUTTON_TEXTURE = Identifier("textures/gui/recipe_button.png")
|
||||
val TEXTURE_HEIGHT = 198
|
||||
val TEXTURE_WIDTH = 176
|
||||
}
|
||||
|
||||
override fun init() {
|
||||
super.init()
|
||||
narrow = width < 379
|
||||
val recipeBookOffsetX = 12
|
||||
val recipeBookOffsetY = 40
|
||||
recipeBook.initialize(width, height, client, narrow, handler as AbstractRecipeScreenHandler<*>)
|
||||
x = recipeBook.findLeftEdge(width, TEXTURE_WIDTH)
|
||||
addDrawableChild(
|
||||
TexturedButtonWidget(x + recipeBookOffsetX, height / 2 - recipeBookOffsetY, 20, 18, 0, 0, 19, RECIPE_BUTTON_TEXTURE) {
|
||||
recipeBook.toggleOpen()
|
||||
x = recipeBook.findLeftEdge(width, TEXTURE_WIDTH)
|
||||
it.setPosition(x + recipeBookOffsetX, height / 2 - recipeBookOffsetY)
|
||||
}
|
||||
)
|
||||
addSelectableChild(recipeBook)
|
||||
setInitialFocus(recipeBook)
|
||||
titleX = 39
|
||||
titleY = -10
|
||||
playerInventoryTitleX = 8
|
||||
playerInventoryTitleY = 88
|
||||
}
|
||||
|
||||
override fun handledScreenTick() {
|
||||
super.handledScreenTick()
|
||||
recipeBook.update()
|
||||
}
|
||||
|
||||
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
renderBackground(context)
|
||||
if (recipeBook.isOpen() && narrow) {
|
||||
drawBackground(context, delta, mouseX, mouseY)
|
||||
recipeBook.render(context, mouseX, mouseY, delta)
|
||||
} else {
|
||||
recipeBook.render(context, mouseX, mouseY, delta)
|
||||
super.render(context, mouseX, mouseY, delta)
|
||||
recipeBook.drawGhostSlots(context, x, y, true, delta)
|
||||
}
|
||||
drawMouseoverTooltip(context, mouseX, mouseY)
|
||||
recipeBook.drawTooltip(context, x, y, mouseX, mouseY)
|
||||
}
|
||||
|
||||
override fun drawBackground(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
|
||||
val i = x
|
||||
val j = (height - TEXTURE_HEIGHT) / 2
|
||||
|
||||
blitk(
|
||||
matrixStack = context.matrices,
|
||||
texture = TEXTURE,
|
||||
x = x, // horizontal placement of GUI
|
||||
y = (height - TEXTURE_HEIGHT) / 2, // vertical placement of GUI
|
||||
|
||||
width = TEXTURE_WIDTH * 1.0, // scale of the GUI width
|
||||
height = TEXTURE_HEIGHT * 1.0 // scale of the GUI height
|
||||
)
|
||||
|
||||
//context.drawTexture(TEXTURE, i, j, 0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT)
|
||||
}
|
||||
|
||||
override fun isPointWithinBounds(x: Int, y: Int, width: Int, height: Int, pointX: Double, pointY: Double): Boolean {
|
||||
return (!narrow || !recipeBook.isOpen()) && super.isPointWithinBounds(x, y, width, height, pointX, pointY)
|
||||
}
|
||||
|
||||
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
|
||||
if (recipeBook.mouseClicked(mouseX, mouseY, button)) {
|
||||
focused = recipeBook
|
||||
return true
|
||||
}
|
||||
return if (narrow && recipeBook.isOpen()) {
|
||||
true
|
||||
} else super.mouseClicked(mouseX, mouseY, button)
|
||||
}
|
||||
|
||||
override fun isClickOutsideBounds(mouseX: Double, mouseY: Double, left: Int, top: Int, button: Int): Boolean {
|
||||
val bl = mouseX < left.toDouble() || mouseY < top.toDouble() || mouseX >= (left + backgroundWidth).toDouble() || mouseY >= (top + backgroundHeight).toDouble()
|
||||
return recipeBook.isClickOutsideBounds(mouseX, mouseY, x, y, backgroundWidth, backgroundHeight, button) && bl
|
||||
}
|
||||
|
||||
override fun onMouseClick(slot: Slot?, slotId: Int, button: Int, actionType: SlotActionType?) {
|
||||
super.onMouseClick(slot, slotId, button, actionType)
|
||||
recipeBook.slotClicked(slot)
|
||||
}
|
||||
|
||||
override fun refreshCookBook() {
|
||||
recipeBook.refresh()
|
||||
}
|
||||
|
||||
override fun getCookBookWidget(): CookBookWidget {
|
||||
return recipeBook
|
||||
}
|
||||
}
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
package com.cobblemon.mod.common.gui.cooking
|
||||
|
||||
import com.mojang.blaze3d.systems.RenderSystem
|
||||
import net.fabricmc.api.EnvType
|
||||
import net.fabricmc.api.Environment
|
||||
import net.minecraft.client.MinecraftClient
|
||||
import net.minecraft.client.gui.DrawContext
|
||||
import net.minecraft.client.gui.screen.recipebook.RecipeBookWidget
|
||||
import net.minecraft.client.gui.widget.ToggleButtonWidget
|
||||
import net.minecraft.client.recipebook.ClientRecipeBook
|
||||
import net.minecraft.client.recipebook.RecipeBookGroup
|
||||
import net.minecraft.client.render.item.ItemRenderer
|
||||
import net.minecraft.item.ItemStack
|
||||
import net.minecraft.recipe.Recipe
|
||||
import net.minecraft.screen.AbstractRecipeScreenHandler
|
||||
|
||||
@Environment(EnvType.CLIENT)
|
||||
class CookingRecipeGroupButtonWidget(val category: RecipeBookGroup) : ToggleButtonWidget(0, 0, 35, 27, false) {
|
||||
private var bounce = 0f
|
||||
|
||||
init {
|
||||
setTextureUV(153, 2, 35, 0, CookBookWidget.TEXTURE)
|
||||
}
|
||||
|
||||
fun checkForNewRecipes(client: MinecraftClient) {
|
||||
val clientRecipeBook = client.player?.recipeBook ?: return
|
||||
val list = clientRecipeBook.getResultsForGroup(this.category)
|
||||
if (client.player?.currentScreenHandler is AbstractRecipeScreenHandler<*>) {
|
||||
for (recipeResultCollection in list) {
|
||||
for (recipe in recipeResultCollection.getResults(clientRecipeBook.isFilteringCraftable(client.player!!.currentScreenHandler as AbstractRecipeScreenHandler<*>))) {
|
||||
if (clientRecipeBook.shouldDisplay(recipe)) {
|
||||
this.bounce = 15.0f
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderButton(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
if (this.bounce > 0.0f) {
|
||||
val f = 1.0f + 0.1f * Math.sin((this.bounce / 15.0f * Math.PI).toDouble()).toFloat()
|
||||
context.matrices.push()
|
||||
context.matrices.translate((this.x + 8).toFloat(), (this.y + 12).toFloat(), 0.0f)
|
||||
context.matrices.scale(1.0f, f, 1.0f)
|
||||
context.matrices.translate(-(this.x + 8).toFloat(), -(this.y + 12).toFloat(), 0.0f)
|
||||
}
|
||||
|
||||
val minecraftClient = MinecraftClient.getInstance()
|
||||
RenderSystem.disableDepthTest()
|
||||
var i = this.u
|
||||
var j = this.v
|
||||
if (this.toggled) {
|
||||
i += this.pressedUOffset
|
||||
}
|
||||
if (this.isSelected) {
|
||||
j += this.hoverVOffset
|
||||
}
|
||||
|
||||
var k = this.x
|
||||
if (this.toggled) {
|
||||
k -= 2
|
||||
}
|
||||
|
||||
context.drawTexture(this.texture, k, this.y, i, j, this.width, this.height)
|
||||
RenderSystem.enableDepthTest()
|
||||
this.renderIcons(context, minecraftClient.itemRenderer)
|
||||
if (this.bounce > 0.0f) {
|
||||
context.matrices.pop()
|
||||
this.bounce -= delta
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderIcons(context: DrawContext, itemRenderer: ItemRenderer) {
|
||||
val list = this.category.getIcons()
|
||||
val i = if (this.toggled) -2 else 0
|
||||
if (list.size == 1) {
|
||||
context.drawItemWithoutEntity(list[0], this.x + 9 + i, this.y + 5)
|
||||
} else if (list.size == 2) {
|
||||
context.drawItemWithoutEntity(list[0], this.x + 3 + i, this.y + 5)
|
||||
context.drawItemWithoutEntity(list[1], this.x + 14 + i, this.y + 5)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCategoryValue(): RecipeBookGroup {
|
||||
return this.category
|
||||
}
|
||||
|
||||
fun hasKnownRecipes(recipeBook: ClientRecipeBook): Boolean {
|
||||
val list = recipeBook.getResultsForGroup(this.category)
|
||||
this.visible = false
|
||||
if (list != null) {
|
||||
for (recipeResultCollection in list) {
|
||||
if (recipeResultCollection.isInitialized && recipeResultCollection.hasFittingRecipes()) {
|
||||
this.visible = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
return this.visible
|
||||
}
|
||||
}
|
||||
+1
-2
@@ -10,10 +10,9 @@ package com.cobblemon.mod.common.integration.jei.berry
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.integration.jei.CobblemonJeiProvider
|
||||
import com.cobblemon.mod.common.item.BerryItem
|
||||
import com.cobblemon.mod.common.item.berry.BerryItem
|
||||
import mezz.jei.api.registration.IRecipeCategoryRegistration
|
||||
import mezz.jei.api.registration.IRecipeRegistration
|
||||
import net.minecraft.item.ItemStack
|
||||
|
||||
class BerryMutationProvider : CobblemonJeiProvider {
|
||||
override fun registerCategory(registration: IRecipeCategoryRegistration) {
|
||||
|
||||
+1
-1
@@ -8,7 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.integration.jei.berry
|
||||
|
||||
import com.cobblemon.mod.common.item.BerryItem
|
||||
import com.cobblemon.mod.common.item.berry.BerryItem
|
||||
|
||||
//Dont make these ItemStacks, they do not have consistent hash codes, breaks stuff
|
||||
data class BerryMutationRecipe(
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.block.BugwortBlock
|
||||
import com.cobblemon.mod.common.block.SaccharineSaplingBlock
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.item.AliasedBlockItem
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
|
||||
class BugwortItem(block: BugwortBlock) : AliasedBlockItem(block, Settings()) {
|
||||
|
||||
override fun getPlacementState(context: ItemPlacementContext): BlockState? {
|
||||
|
||||
return super.getPlacementState(context)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.item
|
||||
|
||||
import com.cobblemon.mod.common.block.SaccharineSaplingBlock
|
||||
import net.minecraft.block.BlockState
|
||||
import net.minecraft.item.AliasedBlockItem
|
||||
import net.minecraft.item.ItemPlacementContext
|
||||
|
||||
class SaccharineSaplingItem(block: SaccharineSaplingBlock) : AliasedBlockItem(block, Settings()) {
|
||||
|
||||
override fun getPlacementState(context: ItemPlacementContext): BlockState? {
|
||||
|
||||
return super.getPlacementState(context)
|
||||
}
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user