mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-22 23:26:25 -04:00
Compare commits
280
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2f002d630 | ||
|
|
5b9d5cbb55 | ||
|
|
ff890307c2 | ||
|
|
dda9d3e790 | ||
|
|
1acc02a2ec | ||
|
|
9a036a4c01 | ||
|
|
3cea6dd175 | ||
|
|
2fa053f833 | ||
|
|
ea4f858919 | ||
|
|
d86021e2a1 | ||
|
|
79faadef59 | ||
|
|
9b6629db7f | ||
|
|
d151a1ce65 | ||
|
|
d9ca7b483d | ||
|
|
708f7fc1b8 | ||
|
|
621db85845 | ||
|
|
b09066c580 | ||
|
|
7c45f28827 | ||
|
|
9c1b2cae22 | ||
|
|
16b2f81bd4 | ||
|
|
108f0e3d88 | ||
|
|
4344c127eb | ||
|
|
5955547c6f | ||
|
|
7a3a1cb8de | ||
|
|
1674cb0913 | ||
|
|
152ce4e923 | ||
|
|
3a6d024217 | ||
|
|
f2fcc5ba79 | ||
|
|
db0fb8e287 | ||
|
|
dae36a67d1 | ||
|
|
6f99862288 | ||
|
|
0377747e1e | ||
|
|
6bc974f90f | ||
|
|
aca53de259 | ||
|
|
41bde38727 | ||
|
|
a4b7ee3b38 | ||
|
|
154b653c57 | ||
|
|
04489bc969 | ||
|
|
25499b3e7a | ||
|
|
bb6bda54dc | ||
|
|
9f3dbdc2d8 | ||
|
|
932148338c | ||
|
|
a0e732dc6e | ||
|
|
309f674853 | ||
|
|
ea20c2aa4c | ||
|
|
da5272c473 | ||
|
|
dc705c7720 | ||
|
|
a785e574e0 | ||
|
|
4b236b0268 | ||
|
|
1cd86ae5d6 | ||
|
|
b7489a0b14 | ||
|
|
f7554425dc | ||
|
|
9bfc577444 | ||
|
|
858d2e908a | ||
|
|
b61e0b0c82 | ||
|
|
4d9a6e71c9 | ||
|
|
a0010de504 | ||
|
|
cb73a30e23 | ||
|
|
3ca6d350fe | ||
|
|
2997ce8457 | ||
|
|
ff279d8310 | ||
|
|
9a7536b068 | ||
|
|
bd973e702e | ||
|
|
ede1851d34 | ||
|
|
cdb8ee0d8e | ||
|
|
e7d86a382c | ||
|
|
eb911df830 | ||
|
|
1c30acc53c | ||
|
|
32b0699235 | ||
|
|
e9c6580230 | ||
|
|
f893eb48fd | ||
|
|
232e2f18bf | ||
|
|
c266c99710 | ||
|
|
2a0e8f6d47 | ||
|
|
e6bd8ec529 | ||
|
|
911a95e873 | ||
|
|
447cfc2ae5 | ||
|
|
8dfeb0d260 | ||
|
|
caa7b19833 | ||
|
|
91d6fb62ba | ||
|
|
8f4a5b2015 | ||
|
|
07604c9a3b | ||
|
|
64dd8ae193 | ||
|
|
156ee6f290 | ||
|
|
fe433d1417 | ||
|
|
0dc662d295 | ||
|
|
081e370bfd | ||
|
|
3ef0567287 | ||
|
|
3cad9e5481 | ||
|
|
dc8b7908be | ||
|
|
85085a1eca | ||
|
|
bff59dee6e | ||
|
|
bb5828531a | ||
|
|
cd08e98c28 | ||
|
|
bf7d40c9b5 | ||
|
|
5abb3d7f97 | ||
|
|
975ad8186a | ||
|
|
26b6419f04 | ||
|
|
331f7ca425 | ||
|
|
71c9ac0ff0 | ||
|
|
1404cc0ab5 | ||
|
|
6ca908a91d | ||
|
|
0948b8da9a | ||
|
|
22e04a53b3 | ||
|
|
df3217b41b | ||
|
|
9ff15a1913 | ||
|
|
89e43945a3 | ||
|
|
bd8dcddf35 | ||
|
|
dd372f3160 | ||
|
|
c56aab28d4 | ||
|
|
ca58801794 | ||
|
|
38fa5e2628 | ||
|
|
fae1ae7124 | ||
|
|
3ec5f76c73 | ||
|
|
5bb8aaba55 | ||
|
|
23ba41e981 | ||
|
|
29b049452b | ||
|
|
0b0f665804 | ||
|
|
3cdf28ffdd | ||
|
|
5d53fba2ce | ||
|
|
c6fb18ea51 | ||
|
|
2d851cb134 | ||
|
|
f685be0aab | ||
|
|
14cc851562 | ||
|
|
7223d1fd68 | ||
|
|
6ab5ad52ec | ||
|
|
079b2ae766 | ||
|
|
34eea827e7 | ||
|
|
06c6ee5a9c | ||
|
|
54748c20ea | ||
|
|
75f52e4cf4 | ||
|
|
55446dd4b1 | ||
|
|
02e63b6fc8 | ||
|
|
3aebcf66ec | ||
|
|
49a5f716b7 | ||
|
|
8b825d558f | ||
|
|
9a7f97f11f | ||
|
|
95b8d62e4a | ||
|
|
0bd7b4dab3 | ||
|
|
8cbada0cf5 | ||
|
|
f0784fec2d | ||
|
|
cfe0c5e0a5 | ||
|
|
0bc5bb5caa | ||
|
|
3330941a2c | ||
|
|
79ea082d7e | ||
|
|
3d78929ff0 | ||
|
|
5d94b03f7a | ||
|
|
587305eb52 | ||
|
|
e363ce5e0e | ||
|
|
e49b704200 | ||
|
|
ec8b35860c | ||
|
|
74c49bf2da | ||
|
|
4354c3fb03 | ||
|
|
c03e7d9ed3 | ||
|
|
b54f2442c9 | ||
|
|
62c0545ea7 | ||
|
|
f25a676bd5 | ||
|
|
a9906762a0 | ||
|
|
7b5b88eca2 | ||
|
|
f821abd7e5 | ||
|
|
291682ba52 | ||
|
|
546d403b45 | ||
|
|
2652e53e0c | ||
|
|
08ac7d478d | ||
|
|
4f51016ca9 | ||
|
|
ef8b4fd3cd | ||
|
|
f55972c190 | ||
|
|
2dbf304ebe | ||
|
|
1c5feab9c9 | ||
|
|
1c720793c4 | ||
|
|
6a170f8ce8 | ||
|
|
b4aba94f89 | ||
|
|
9832730f5c | ||
|
|
ffdaec3e15 | ||
|
|
d481f887a8 | ||
|
|
df3aec6222 | ||
|
|
b423fb1c46 | ||
|
|
8ae26a268a | ||
|
|
39b522abcc | ||
|
|
9bcfcef4fd | ||
|
|
60ac2d8058 | ||
|
|
9366d28141 | ||
|
|
75b48196b7 | ||
|
|
5180e2b501 | ||
|
|
bb407b1d65 | ||
|
|
8b308e5595 | ||
|
|
148526e5e8 | ||
|
|
f319efcf12 | ||
|
|
c9f40e32a1 | ||
|
|
d642b8dd07 | ||
|
|
549a11116e | ||
|
|
b5468a4a4f | ||
|
|
5055e3926c | ||
|
|
3d92de18b8 | ||
|
|
c5d278ccb7 | ||
|
|
bf5e15de45 | ||
|
|
f4af417267 | ||
|
|
69c04acaeb | ||
|
|
83a6bb188f | ||
|
|
494a8b5e91 | ||
|
|
c8021f2f08 | ||
|
|
8cdf3af047 | ||
|
|
c90a24e1ab | ||
|
|
83e3cfc34e | ||
|
|
133dd4e5b7 | ||
|
|
a8cca14b86 | ||
|
|
9b0f356fa4 | ||
|
|
f2b0a102b6 | ||
|
|
ab9851fd12 | ||
|
|
a9df6bc709 | ||
|
|
a7d01b230a | ||
|
|
2a7db4b9b8 | ||
|
|
77e5f2501d | ||
|
|
3681813976 | ||
|
|
e6513717bd | ||
|
|
0e0c98f693 | ||
|
|
528bfa4e22 | ||
|
|
83c87779d7 | ||
|
|
dc33235144 | ||
|
|
333d234dbe | ||
|
|
935c658c5f | ||
|
|
ee42825dc3 | ||
|
|
0d9a4f9392 | ||
|
|
0426f4fbf9 | ||
|
|
5e90c05fdd | ||
|
|
75bc03fdcb | ||
|
|
9a0648d956 | ||
|
|
91187ce952 | ||
|
|
686bc62996 | ||
|
|
5925247ace | ||
|
|
c6a4bec3f3 | ||
|
|
7e582090f8 | ||
|
|
55eef6ea69 | ||
|
|
f65cb9538c | ||
|
|
8dbce8fefe | ||
|
|
46e771a062 | ||
|
|
4c0095844b | ||
|
|
4eb39566fa | ||
|
|
d034469ebb | ||
|
|
4f8a44c95b | ||
|
|
52b17ca39e | ||
|
|
10a2ef27d6 | ||
|
|
309ff3da13 | ||
|
|
3af91e2a74 | ||
|
|
2e027e3db7 | ||
|
|
fc40fb02c0 | ||
|
|
2c1c0299f9 | ||
|
|
3c7637515c | ||
|
|
08cbbaf68c | ||
|
|
6e911acf01 | ||
|
|
f6d82a1b28 | ||
|
|
702126aeab | ||
|
|
df566e8fcb | ||
|
|
c83e0f33b5 | ||
|
|
f279f936b6 | ||
|
|
f8e20bafd9 | ||
|
|
a5211ca7ff | ||
|
|
0a5abdb331 | ||
|
|
4ed7413dd2 | ||
|
|
40b66c24fc | ||
|
|
2f7bedf001 | ||
|
|
44c31a9ddb | ||
|
|
20a35de3d8 | ||
|
|
cfc8a15db1 | ||
|
|
d5ba22a86c | ||
|
|
a38505f1c5 | ||
|
|
3cf1ba1fb3 | ||
|
|
9f5d595827 | ||
|
|
75673f0204 | ||
|
|
b43a7571ff | ||
|
|
b071dc04d3 | ||
|
|
12f9ff1647 | ||
|
|
3256a302df | ||
|
|
edbe447477 | ||
|
|
2f43910368 | ||
|
|
c2e87e4cfa | ||
|
|
e40d2ee806 | ||
|
|
ba74e7603b | ||
|
|
19c3fe6dc4 | ||
|
|
d0778dd124 |
@@ -157,6 +157,9 @@ build:
|
||||
paths:
|
||||
- neoforge/build/libs
|
||||
- fabric/build/libs
|
||||
exclude:
|
||||
- neoforge/build/libs/*-dev-shadow.jar
|
||||
- fabric/build/libs/*-dev-shadow.jar
|
||||
|
||||
publish:
|
||||
stage: deploy
|
||||
|
||||
+110
-494
@@ -1,529 +1,145 @@
|
||||
# Changelog
|
||||
## [1.8.0 - Make Your Move Update (September 6th, 2026)](#1-8-0)
|
||||
#### I think the red eyes mean they're friendly!
|
||||
## [1.8.2 (MONTH xth, 2026)](#1-8-2)
|
||||
|
||||
### Additions
|
||||
- Added Alpha Pokémon
|
||||
- Alpha Pokémon are bigger, stronger, and more aggressive than regular Pokémon.
|
||||
- They are also scarier.
|
||||
- They always know a TM move and will adapt to be stronger than your party.
|
||||
- Alphas will always lead herds and spawn in herds.
|
||||
- Added alpha Pokémon attraction bait effect to Hopo Berries.
|
||||
- Added intrinsic scaling to all Pokémon (anywhere from 5% smaller to 5% bigger than normal). Some cases, such as shoulder mounting, will not scale so that the shoulder animations don't look silly.
|
||||
- Added scaling to "baby" Pokémon (10% smaller at level 1 → normally sized at level 10).
|
||||
- Added TMs.
|
||||
- Added TM Machine. TMs are made from elemental type gems, and their recipes are unlocked by having Pokémon that know the move.
|
||||
- Added config options for making TMs infinite-use and instant unlocking of all TM recipes.
|
||||
- Added Type Gem blocks as well as crystal clusters and deepslate core blocks that are used for growing elemental type gems.
|
||||
- Added a new Starter Selection menu that looks way better.
|
||||
- Added a "Move Dex" to the Pokédex to help identify what moves a Pokémon can learn and what Pokémon can get a move.
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added 49 new habitat structures. In these habitats you will find rarer Pokémon and sometimes different Pokémon will appear on different days.
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys? It's true!!!
|
||||
- Added a way to obtain Porygon by inserting an Upgrade or Dubious Disc into a data monitor.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks. What does that have to do with Pokémon? How about you mind your own business!
|
||||
- Added missing crops to Botany Pots integration.
|
||||
- Added conditional seats for riding. Seats can be gated by Molang such as "is_alpha".
|
||||
- Added 10 new ruin structures.
|
||||
- Added a new shipwreck cove structure called the Magma Shipwreck Cove.
|
||||
- Added 7 new advancements.
|
||||
- Pokémon cries now play when clicking Pokémon in the summary screen.
|
||||
- Held item visibility can now be controlled by the `held_item_visible` command argument.
|
||||
- Added config option to disable Pokémon spawning on a per-world basis.
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`).
|
||||
- Added `/givetm` command.
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
|
||||
|
||||
### New rideable Pokémon
|
||||
- Crobat
|
||||
- Skarmory
|
||||
- Milotic
|
||||
- Archeops
|
||||
- Goodra
|
||||
- Goodra (Hisuian Form)
|
||||
- Drampa
|
||||
- Duraludon
|
||||
- Archaludon
|
||||
- The following items can now be placed on the ground as blocks:
|
||||
- King's Rock, Galarica Cuff, Unremarkable Teacup, Masterpiece Teacup, Chipped Pot, Cracked Pot, Sachet, Metal Powder, Quick Powder, Bright Powder, Icy Rock, Heat Rock, Smooth Rock, Damp Rock, Destiny Knot, Luminous Moss, Never-Melt Ice, Wide Lens, Scope Lens, and Zoom Lens.
|
||||
- Iron Ball, Metal Coat, Throat Spray, HP Up, Protein, Iron, Carbos, Calcium, Zinc, PP Up, PP Max, Upgrade, Dubious Disc, and Loaded Dice.
|
||||
- The following items can now be placed on various surfaces:
|
||||
- Galarica Wreath, Magnet, Flame Orb, Life Orb, Toxic Orb, Light Ball, and Smoke Ball.
|
||||
- The following items can now be placed as blocks and have additional functionality:
|
||||
- Ring Target, Metronome, and Eject Button.
|
||||
- The following items can now be worn by Players and Pokémon:
|
||||
- Galarica Wreath, Cleanse Tag, Spell Tag, Sharp Beak, Covert Cloak, Shell Helmet, and Never-Melt Ice.
|
||||
- Wearable items can now be equipped by a player by holding it and right-clicking (or rebound use/interact)
|
||||
- Wearable items that can be placed down are exempt from this behaviour.
|
||||
- The evolution tree can now be viewed on the Pokédex, showing a Pokémon's full family line and what's needed to evolve into each stage.
|
||||
- Added custom sounds to a bunch of items that can be placed as blocks: liquid bottles, pill bottles, powders and dice.
|
||||
|
||||
### Pokémon Added
|
||||
#### Generation 3
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Seviper
|
||||
- Zangoose
|
||||
|
||||
#### Generation 4
|
||||
- Cherubi
|
||||
- Cherrim
|
||||
- Stunky
|
||||
- Skuntank
|
||||
- Snover
|
||||
- Abomasnow
|
||||
- Burmy
|
||||
- Wormadam
|
||||
- Mothim
|
||||
|
||||
#### Generation 5
|
||||
- Audino
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
- Tympole
|
||||
- Palpitoad
|
||||
- Seismitoad
|
||||
|
||||
#### Generation 6
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
- Helioptile
|
||||
- Heliolisk
|
||||
|
||||
#### Generation 7
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
- Grubbin
|
||||
- Charjabug
|
||||
- Vikavolt
|
||||
- Minior
|
||||
|
||||
#### Generation 8
|
||||
- Rolycoly
|
||||
- Carkol
|
||||
- Coalossal
|
||||
- Runerigus
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Indeedee
|
||||
- Duraludon
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
- Eternatus
|
||||
|
||||
#### Generation 9
|
||||
|
||||
- Nymble
|
||||
- Lokix
|
||||
- Kingambit
|
||||
- Dipplin
|
||||
- Archaludon
|
||||
- Hydrapple
|
||||
|
||||
#### Regional Forms
|
||||
- Sandshrew (Alolan Form)
|
||||
- Sandslash (Alolan Form)
|
||||
- Yamask (Galarian Form)
|
||||
- Avalugg (Hisuian Form)
|
||||
- Greavard
|
||||
- Houndstone
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
|
||||
- Sandshrew
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Ditto
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Skarmory
|
||||
- Smoochum
|
||||
- Jynx
|
||||
- Popplio
|
||||
- Brionne
|
||||
- Primarina
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Nacli
|
||||
- Naclstack
|
||||
- Garganacl
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Tyranitar
|
||||
- Turtonator
|
||||
- Golett
|
||||
- Corsola
|
||||
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
|
||||
- ... and Substitute
|
||||
|
||||
#### Model changes to the regional bias resource pack:
|
||||
- Added Mime Jr. (Galar Bias)
|
||||
- Added Bergmite (Hisui Bias)
|
||||
- Changed models for every existing regional bias
|
||||
- Sudowoodo
|
||||
- Spheal
|
||||
- Cacnea
|
||||
- Cacturne
|
||||
- Binacle
|
||||
- Barbaracle
|
||||
- Glimmet
|
||||
- Glimmora
|
||||
- Deerling
|
||||
- Sawsbuck (All forms)
|
||||
- Absol
|
||||
- Poliwag
|
||||
- Politoed
|
||||
- Ekans
|
||||
- Oddish
|
||||
- Gloom
|
||||
- Vileplume
|
||||
- Bellossom
|
||||
- Shroomish
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
|
||||
- Kakuna
|
||||
- Grimer
|
||||
- Nidoran♀ (+ New shoulder mount)
|
||||
- Nidorina
|
||||
- Nidoran♂ (+ New shoulder mount)
|
||||
- Sandshrew
|
||||
- Diglett
|
||||
- Dugtrio
|
||||
- Graveler
|
||||
- Happiny
|
||||
- Chansey
|
||||
- Blissey
|
||||
- Poliwhirl
|
||||
- Poliwrath
|
||||
- Zubat
|
||||
- Vulpix
|
||||
- Drowzee
|
||||
- Hypno
|
||||
- Noctowl
|
||||
- Skarmory
|
||||
- Skitty
|
||||
- Flygon
|
||||
- Shellos
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Joltik
|
||||
- Galvantula
|
||||
- Nidoking
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Archeops
|
||||
- Drampa
|
||||
- Sewaddle
|
||||
- Swadloon
|
||||
- Leavanny
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Shedinja
|
||||
- Skwovet
|
||||
- Greedent
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Luvdisc
|
||||
- Staryu
|
||||
- Starmie
|
||||
- Togetic
|
||||
- Rellor
|
||||
- Rabsca
|
||||
- Yungoos
|
||||
- Gumshoos
|
||||
- Cottonee
|
||||
- Whimsicott
|
||||
- Corsola
|
||||
|
||||
#### Added sounds to the following moves
|
||||
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Draco Meteor
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
- Flame Wheel
|
||||
- Giga Drain
|
||||
- Ice Punch
|
||||
- Leech Life
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Poison Gas
|
||||
- Rock Throw
|
||||
- Scary Face
|
||||
- Seed bomb
|
||||
- Smokescreen
|
||||
- String Shot
|
||||
- Tail Whip
|
||||
- Treecko (New Shoulder Mount!)
|
||||
- Bonsly (New Shoulder Mount!)
|
||||
- Sudowoodo
|
||||
- Cacnea
|
||||
- Cacturne
|
||||
- Ekans
|
||||
- Arbok
|
||||
- Oddish
|
||||
- Gloom
|
||||
- Vileplume
|
||||
- Bellossom
|
||||
|
||||
### Changes
|
||||
- Removed the penalty for block destruction speed when on a hover mount, making them better for digging.
|
||||
- Golden Apples can now be crafted using the Applin Apples.
|
||||
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes.
|
||||
- Updated Pokédex Screens item tag to support tagged variants of Redstone and Glowstone Dust.
|
||||
- Sharp Beak can now be crafted using bones and Flying Gems.
|
||||
- Metal Alloy can now be crafted using iron and Thunder Stones.
|
||||
- Ability Capsules can now be brewed from Medicinal Brew and Dragon Breath.
|
||||
- Using an Ability Capsule will now return an Empty Bottle.
|
||||
- Decreased the default pastured wander radius to 32 blocks.
|
||||
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release.
|
||||
- Rooted Arch Ruins, Crumbling Arch Ruins, and all fossil structures have been improved so that they generate more naturally in terrain.
|
||||
- Gimmighoul towers now generate at ground-level rather than sinking one block into the ground.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- The Prehistoric Underwater Fissure and Prehistoric Submerged Impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves more easily.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma Cove gets Blast Burn, Lush Cove gets Frenzy Plant, and Submerged Cove gets Hydro Cannon.)
|
||||
- The following items can now be obtained by brushing the following Pokémon:
|
||||
- Pink Wool - Wigglytuff
|
||||
- White Wool - Swablu, Altaria
|
||||
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
|
||||
- Phantom Membrane - Dreepy, Drakloak, Dragapult
|
||||
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
|
||||
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
|
||||
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
|
||||
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples.
|
||||
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame.
|
||||
- Refactored all advancements, removing several deemed unnecessary. Due to this cleanup, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). This sucks, and we're sorry. We had to do this eventually so better now than later.
|
||||
- Forces between colliding hitboxes now factors in the weight of Pokémon.
|
||||
- Improved berry tree performance on singleplayer and servers.
|
||||
- Updated stat increase and decrease particle effects with the addition of alpha boost particles.
|
||||
- Improved the performance of pasture blocks on singleplayer and servers.
|
||||
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts.
|
||||
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level. No more free lunches with Pokémon that can't even hurt you.
|
||||
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen.
|
||||
- The party overlay's experience-gained animation now displays the amount of EXP the Pokémon gained.
|
||||
- Pressing the inventory key now closes the Pokémon interact wheel GUI.
|
||||
- Updated the sound for harvesting Apricorns.
|
||||
- Lowered the volume of harvesting berries.
|
||||
- Improved compatibility with various mods
|
||||
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests.
|
||||
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks.
|
||||
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones.
|
||||
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks.
|
||||
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used.
|
||||
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles.
|
||||
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart.
|
||||
- Amendments allows for cooking Leek and Potato Stew in a Cauldron.
|
||||
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments).
|
||||
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy.
|
||||
- Berry Trees are now compatible with the Planter properly
|
||||
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues.
|
||||
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items.
|
||||
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets.
|
||||
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create.
|
||||
- Rage Candy Bar has a Create Mixing recipe.
|
||||
- All Cobblemon ores can now be crushed in Create's Crushing Wheels. All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems.
|
||||
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed.
|
||||
- Big Roots and Revival Herbs can be milled to their respective powders.
|
||||
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops.
|
||||
- Auspicious Armour and Upgrades can be Haunted using Create's crafting systems.
|
||||
- Applin Apples can be used to make Honeyed Apples in Create.
|
||||
- Sounds for interacting with various Cobblemon blocks now play from the centre of the block, rather than the corner. You probably didn't notice.
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot.
|
||||
- Adjusted the walk speed for Croagunk and Aerodactyl.
|
||||
- The `/pc` command no longer plays PC activation sounds
|
||||
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens.
|
||||
- Ice type Pokémon are now immune to Packed Snow.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
- Updated various recipes to display in Recipe Groups within the Recipe Book
|
||||
- Updated most recipes to display more logically/consistently within the Recipe Book's categories
|
||||
- Make ride rotations less jittery in multiplayer.
|
||||
- Changes tm shelf discs to use textures instead of tinting and overlays to line up with item display
|
||||
- Blank TM can now be inserted into the Disc Shelf
|
||||
- Recall animation now renders ball in hand with the player arm raised.
|
||||
- Made regional and Vivillon split-evo locations now mutually exclusive
|
||||
- Item evolutions now unlock **all** locked evolutions whose requirements are met instead of stopping after the first.
|
||||
- Exceptions being "forced" evolutions which will continue to stop after the first one
|
||||
|
||||
### Fixes
|
||||
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
|
||||
- Fixed Omanyte and Omastar incorrectly using water animations on land.
|
||||
- Fixed Pokédex layout for genderless variations.
|
||||
- Fixed odd stacking behaviour with Poké Baits.
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed an issue where particles like beams can disappear when you don't see the beginning of the beam, or you look away.
|
||||
- Fixed Bedrock particle materials sometimes being incorrectly invisible from some sides.
|
||||
- Fixed an occasional crash on retrieving revived fossils.
|
||||
- Fixed brushing not working on Neoforge. Again.
|
||||
- Fixed braking not working for submarine-style mounts (whoops).
|
||||
- Fixed strafing not having the same friction as forward movement (funny whoops).
|
||||
- Fixed submarine-style mounts' surface-water animations not playing when supposed to.
|
||||
- Fixed submarine-style mounts' camera freaking out when looking up or down on the surface.
|
||||
- Fixed pastured Pokémon despawning and running away.
|
||||
- Fixed Apricorn trees crashing when other mods bonemeal them.
|
||||
- Fixed the issue that caused hover-style mounts without a ground style to be rendered useless upon touching the ground on a server.
|
||||
- Fixed horizontal and vertical collisions with hover-style mounts so they no longer accumulate or retain momentum.
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu. Well, I suppose all crashes are unexpected. You know what I mean.
|
||||
- Fixed NPCs switching Pokémon very often during battles.
|
||||
- Fixed Campfire Pot causing crashes under certain circumstances.
|
||||
- Fixed Type Gems not being consumed upon use in battles.
|
||||
- Fixed several Ancient Pokéball's not using the correct capture multiplier.
|
||||
- Fixed a server warning that could occur when recalling Pokémon.
|
||||
- Fixed sliding particles incorrectly handling block collision.
|
||||
- Fixed Full Heal not curing status conditions in battle. Literally it's one job.
|
||||
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
|
||||
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation.
|
||||
- Fixed a very niche issue where structures would spawn in an incorrect location, creating very funky world gen.
|
||||
- Fixed Galarian Ponyta missing its cry.
|
||||
- Fixed various log warnings caused by redundant files and sound events.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT.
|
||||
- Tumblestone Clusters now display correctly on maps.
|
||||
- Fixed removing mulch from berry trees with a shovel not working. That was meant to be a thing.
|
||||
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full.
|
||||
- Fixed Stripped Saccharine Wood recipe being... not there.
|
||||
- Fixed some performance issues with a kind of sound event causing lag by attempting to create sounds every tick when out of range of hearing.
|
||||
- Fixed stat-reducing Aprijuice not being possible to apply on a maxed out Pokémon. Also fixed the possibility of going into negatives.
|
||||
- Fixed exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so.
|
||||
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models.
|
||||
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option.
|
||||
- Fixed gilded chests being more accessible than intended.
|
||||
- Fixed fished up Pokémon not being added as "seen" to the Pokédex.
|
||||
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokédex.
|
||||
- Fixed a crash involving berry trees that have been world edited.
|
||||
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks.
|
||||
- Fixed Pokémon entities leveled by rare candy not getting stronger as Minecraft entities unless recalled.
|
||||
- Fixed a freeze during game load that occurred when multiple mods tried to load Cobblemon API classes at the same time.
|
||||
- Lowered the volume of Blitzle and Zebstrika's cry (shut up!).
|
||||
- Regional Foods will now prioritise being eaten instead of used for healing Pokémon - shift clicking will use it as healing.
|
||||
- Resolved z-fighting (texture flickering) on Pokédex Models.
|
||||
- Restored Ninjask's ability to do barrel rolls.
|
||||
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
|
||||
- Fixed a crash caused by adding enchanted items to the Resurrection Machine.
|
||||
- Fixed a crash that could occur when starting a PvN battle with only fainted Pokémon.
|
||||
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod.
|
||||
- Fixed Full Heal block not dropping Full Heals. Lmao.
|
||||
- Fixed status cure items not returning an empty bottle when used.
|
||||
- Fixed Panpour consuming its held item when evolving with a Water Stone.
|
||||
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe. Free bowls!
|
||||
- Fixed `/giveallpokemon` not respecting the min and max arguments.
|
||||
- Fixed the choose starter toast not disappearing when capturing a Pokémon instead of opening the starter selection screen.
|
||||
- Fixed an issue where certain Sinister Tea seasonings were only applying as instant effects and would not apply over time.
|
||||
- Long config option labels now scroll instead of overflowing in the configuration menu.
|
||||
- Fixed some z-fighting and clipping on Dewott and Hisui-Bias Dewott.
|
||||
- Eggant Berries can now be composted properly on NeoForge.
|
||||
- Composting chances are now the same on NeoForge as on Fabric.
|
||||
- Fixed problem where furnaces which had smelted Revival Herbs into Heal Powder in older versions would glitch out on trying to get the experience out of the block.
|
||||
- Fixed the Battle AI not recognizing Surging Strikes as a multistrike move.
|
||||
- Fixed Tentacool not using its swim animation when on water surface.
|
||||
- Fixed Eevee using its left shoulder pose when in battle.
|
||||
- Fixed Magikarp and Gyarados Jump Pattern resourcepacks replacing shiny textures of addons targeting Magikarp or Gyarados.
|
||||
- Fixed Combee's pollinated texture overwriting the shiny and female textures.
|
||||
- Fixed a colour mistake with Dracovish's shiny.
|
||||
- Fixed Togedemaru's back spikes z-fighting.
|
||||
- Fixed Sliggoo (+ Hisuian) evolving in the Nether and End where it can't actually rain.
|
||||
- Corrected the scales, hitbox sizes, and eye heights for Ariados, Cetoddle, Durant, Galvantula, Klink, Lanturn, Lumineon, Mantine, Mantyke, Mudbray, Numel, Staravia, and Torkoal.
|
||||
- Fixed Torkoal's walk speed not matching its animation. He will no longer move faster than Bunnelby and Buneary. This tortoise would've won.
|
||||
- Fixed Lanturn T-posing due to missing ground walk and sleep animations.
|
||||
- Fixed Oshawott and Dewott using their sleep animation in unintended scenarios.
|
||||
- Fixed Dudunsparce and Maushold always having the same form instead of having the 1 in 100 chance of being different.
|
||||
- Fixed bird mounts rapidly switching between their flying and gliding animations when flying at low speed.
|
||||
- Fixed some battles being startable with fainted Pokémon in the initial sendout.
|
||||
- Fixed random gender selection for forms with strict gender divides (such as Meowstic and Oinkologne) never choosing female (that's sexist).
|
||||
- Fixed the "Press R to battle" prompt not disappearing after you finish your first battle, at least until you relogged.
|
||||
- Fixed the composter block being listed as "composer" in the cosmetic items species feature
|
||||
- Fixed Castform's forms being tagged as introduced in generation 4 instead of 3.
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`.
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly.
|
||||
- Fixed Litleo not blinking.
|
||||
- Correctly grants "They're Minerals!" advancement when planting a type gem
|
||||
- Shoulder mounted Pokémon now follow player body animations, improving their positioning with custom player animations and default actions.
|
||||
- Shoulder mounted Pokémon now sit slightly higher with chest armor equipped for better alignment, with a configurable blacklist.
|
||||
- Type Gem Blocks now have advancements to give you the recipe in the recipe book (the data for the advancements were inside the recipes somehow??)
|
||||
- Some other recipes without advancements have also been corrected
|
||||
- Corrected the TM Recipes for Charge Beam and Payday
|
||||
- Type Gem blocks now properly generate clusters off of the block.
|
||||
- Starter Screen Categories will no longer stop rendering if they are bigger than the viewport, and you are not viewing top or bottom edges
|
||||
- Random Starter option now properly handled in height calculation, so now it won't run off into other categories or be un-accessible due to scroll limits if it causes a new row
|
||||
- Mochi and Feathers no longer drop glass bottles on use
|
||||
- Fixed a rare set of concurrency bugs related to reloading packs on NeoForge; fixes a ModernFix issue
|
||||
- Removed unnecessary conditions for Mimikyu's standing pose. Hopefully fixes the rare instance of it using its shoulder animations during battle in multiplayer scenarios.
|
||||
- Fixed Rockruff line not blinking
|
||||
- Fixed Flygon sometimes using its running animation instead of walking
|
||||
- Fixed Bramblin always sleeping outside of battle
|
||||
- Fixed Dragonair using its battle idle as a normal idle
|
||||
- Fixed Lycanroc's head movement
|
||||
- Added head movement to Archaludon
|
||||
- Fixed Duraludon line not having any sleep animations
|
||||
- Fixed Diglett line not having their faint animations
|
||||
- Fixed Kingambit moving its legs while walking
|
||||
- Fixed Alolan Diglett's cry not playing
|
||||
- Fixed shiny alpha Snorlax using the non-shiny texture
|
||||
- Various Structure fixes
|
||||
- Fixed shiny Pokémon under certain circumstances forgetting taught TMs
|
||||
- Fixed certain Pokémon sounds sometimes being doubled when played at specific times in their animations
|
||||
- Fixed ball capture beam origin coming from behind the ball.
|
||||
- Fixed Safari Ball catch rate outside of battle
|
||||
|
||||
### Developer
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters.
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters.
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts.
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
|
||||
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
|
||||
- Fixed nicknames not being applied to Pokémon spawned from `spawn_pool_world` files.
|
||||
- Added support for `profilePokedexScale`, `profilePokedexTranslation`, `profileSummaryScale`, and `profileSummaryTranslation` in poser files for finer control over profile displays.
|
||||
- Fixed riding camera offsets defined on Pokémon forms not being applied.
|
||||
- Marks can now provide aspects.
|
||||
- Evolution requirements can now set `pokedexIcon` and `pokedexTooltip` directly in their JSON to control how they're shown on the Pokédex's evolution tree, for example:
|
||||
```json
|
||||
{
|
||||
"variant": "attack_defence_ratio",
|
||||
"pokedexIcon": "minecraft:diamond_sword",
|
||||
"pokedexTooltip": "cobblemon.ui.pokedex.requirement.attack_defence_ratio.tooltip"
|
||||
}
|
||||
```
|
||||
- Fixed wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing pokemon to appear without entity link
|
||||
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
|
||||
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
|
||||
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
|
||||
- Added basic chatter NPC Behaviour.
|
||||
- Added `q.item.is_enchanted()`.
|
||||
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`.
|
||||
- Added `has_chosen_starter` and `get_starter_uuid` as available Molang functions.
|
||||
- Added `has_tm_move_unlocked`, `unlock_tm_move`, and `lock_tm_move` as available Molang functions for Players.
|
||||
- Added `marks`, `has_mark`, and `remove_marks` as available Molang functions for Pokémon.
|
||||
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
|
||||
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers.
|
||||
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen.
|
||||
- SpeciesAdditions append vs override now matches the wiki documentation.
|
||||
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance.
|
||||
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. Use type: `minecraft:chest` for the loot table.
|
||||
- Added `q.pokemon.entity` to access the entity of Pokémon.
|
||||
- Flamethrower and Seismic Toss had their action effect JSON cleaned up.
|
||||
- Ride sounds can now be set as exclusive to passengers.
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting.
|
||||
- NPC Trainers can now call a `command` animation when issuing a command in battle.
|
||||
- Added a q.failed function to move action effects.
|
||||
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a pre-offset clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
|
||||
- `q.sound` now accepts three parameters (sound event, volume, and pitch) instead of just the sound event and pitch.
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
|
||||
- Added Molang functions for individual Pokédex ids, wherein a subdex is passed in as a string to `dex_caught_count` etc.
|
||||
|
||||
### Particles
|
||||
|
||||
#### New Move Animations
|
||||
- Blizzard
|
||||
- Disarming Voice
|
||||
- Draco Meteor
|
||||
- Dragon Breath
|
||||
- Dragon Rage
|
||||
- Drain Punch
|
||||
- Fairy Wind
|
||||
- Fire Fang
|
||||
- Gust
|
||||
- Horn Drill
|
||||
- Hydro Pump
|
||||
- Ice Fang
|
||||
- Leafage
|
||||
- Leaf Storm
|
||||
- Light Screen
|
||||
- Night Shade
|
||||
- Play Rough
|
||||
- Pyro Ball
|
||||
- Reflect
|
||||
- Rock Slide
|
||||
- Sonic Boom
|
||||
- Sucker Punch
|
||||
- Supersonic
|
||||
- Thunder Fang
|
||||
|
||||
#### Tweaks and Fixes (Moves)
|
||||
|
||||
- Confusion
|
||||
- Dragon Claw
|
||||
- Eruption
|
||||
- Fire Punch
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Frost Breath
|
||||
- Hypnosis
|
||||
- Ice Punch
|
||||
- Icy Wind
|
||||
- Mud Sport
|
||||
- Poison Fang
|
||||
- Protect
|
||||
- Psychic
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Sand Attack
|
||||
- Scratch
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Spore
|
||||
- String Shot
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunder Wave
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
|
||||
#### Miscellaneous Particles
|
||||
|
||||
- Added Wild Might particles for Alpha Pokémon.
|
||||
- Updated stat increase and decrease particles.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Japanese
|
||||
- German
|
||||
- Spanish
|
||||
- Ecuadorian Spanish
|
||||
- Portuguese
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Traditional Chinese
|
||||
- Korean
|
||||
- Russian
|
||||
@@ -1,9 +0,0 @@
|
||||
- Some kind of particle effect for them? they're meant to have glowing eyes, so maybe some red particles on the eye locator
|
||||
--- global posers? stacked posers?
|
||||
--- red texture seems to be what some people wanted.
|
||||
|
||||
/- shoulder mounts part of it
|
||||
- omni boost
|
||||
- roar when player comes within reach, adjust levels
|
||||
|
||||
- Riding settings to control what the min scale is for a pokemon to be ridable.
|
||||
+5
-2
@@ -1,3 +1,6 @@
|
||||
import utilities.gitBranch
|
||||
import utilities.gitShortCommit
|
||||
|
||||
plugins {
|
||||
id("cobblemon.root-conventions")
|
||||
}
|
||||
@@ -6,6 +9,6 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
|
||||
|
||||
val isSnapshot = project.property("snapshot")?.equals("true") == true
|
||||
if (isSnapshot) {
|
||||
val fixedBranchName = versioning.info.branch.substringAfter("/")
|
||||
version = "$version-${fixedBranchName}-${versioning.info.build}"
|
||||
val fixedBranchName = gitBranch().substringAfter("/")
|
||||
version = "$version-${fixedBranchName}-${gitShortCommit()}"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,529 @@
|
||||
# Changelog
|
||||
## [1.8.0 - Make Your Move Update (September 6th, 2026)](#1-8-0)
|
||||
#### I think the red eyes mean they're friendly!
|
||||
|
||||
### Additions
|
||||
- Added Alpha Pokémon
|
||||
- Alpha Pokémon are bigger, stronger, and more aggressive than regular Pokémon.
|
||||
- They are also scarier.
|
||||
- They always know a TM move and will adapt to be stronger than your party.
|
||||
- Alphas will always lead herds and spawn in herds.
|
||||
- Added alpha Pokémon attraction bait effect to Hopo Berries.
|
||||
- Added intrinsic scaling to all Pokémon (anywhere from 5% smaller to 5% bigger than normal). Some cases, such as shoulder mounting, will not scale so that the shoulder animations don't look silly.
|
||||
- Added scaling to "baby" Pokémon (10% smaller at level 1 → normally sized at level 10).
|
||||
- Added TMs.
|
||||
- Added TM Machine. TMs are made from elemental type gems, and their recipes are unlocked by having Pokémon that know the move.
|
||||
- Added config options for making TMs infinite-use and instant unlocking of all TM recipes.
|
||||
- Added Type Gem blocks as well as crystal clusters and deepslate core blocks that are used for growing elemental type gems.
|
||||
- Added a new Starter Selection menu that looks way better.
|
||||
- Added a "Move Dex" to the Pokédex to help identify what moves a Pokémon can learn and what Pokémon can get a move.
|
||||
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
|
||||
- Added 49 new habitat structures. In these habitats you will find rarer Pokémon and sometimes different Pokémon will appear on different days.
|
||||
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys? It's true!!!
|
||||
- Added a way to obtain Porygon by inserting an Upgrade or Dubious Disc into a data monitor.
|
||||
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks. What does that have to do with Pokémon? How about you mind your own business!
|
||||
- Added missing crops to Botany Pots integration.
|
||||
- Added conditional seats for riding. Seats can be gated by Molang such as "is_alpha".
|
||||
- Added 10 new ruin structures.
|
||||
- Added a new shipwreck cove structure called the Magma Shipwreck Cove.
|
||||
- Added 7 new advancements.
|
||||
- Pokémon cries now play when clicking Pokémon in the summary screen.
|
||||
- Held item visibility can now be controlled by the `held_item_visible` command argument.
|
||||
- Added config option to disable Pokémon spawning on a per-world basis.
|
||||
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`).
|
||||
- Added `/givetm` command.
|
||||
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
|
||||
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
|
||||
|
||||
### New rideable Pokémon
|
||||
- Crobat
|
||||
- Skarmory
|
||||
- Milotic
|
||||
- Archeops
|
||||
- Goodra
|
||||
- Goodra (Hisuian Form)
|
||||
- Drampa
|
||||
- Duraludon
|
||||
- Archaludon
|
||||
|
||||
### Pokémon Added
|
||||
#### Generation 3
|
||||
- Wurmple
|
||||
- Silcoon
|
||||
- Beautifly
|
||||
- Cascoon
|
||||
- Dustox
|
||||
- Skitty
|
||||
- Delcatty
|
||||
- Seviper
|
||||
- Zangoose
|
||||
|
||||
#### Generation 4
|
||||
- Cherubi
|
||||
- Cherrim
|
||||
- Stunky
|
||||
- Skuntank
|
||||
|
||||
#### Generation 5
|
||||
- Audino
|
||||
- Pawniard
|
||||
- Bisharp
|
||||
|
||||
#### Generation 6
|
||||
- Pancham (+ Cosmetic with Black Glasses)
|
||||
- Pangoro (+ Cosmetic with Black Glasses)
|
||||
|
||||
#### Generation 7
|
||||
- Rockruff
|
||||
- Lycanroc
|
||||
|
||||
#### Generation 8
|
||||
- Rolycoly
|
||||
- Carkol
|
||||
- Coalossal
|
||||
- Runerigus
|
||||
- Snom
|
||||
- Frosmoth
|
||||
- Indeedee
|
||||
- Duraludon
|
||||
- Applin
|
||||
- Flapple
|
||||
- Appletun
|
||||
|
||||
#### Generation 9
|
||||
|
||||
- Nymble
|
||||
- Lokix
|
||||
- Kingambit
|
||||
- Dipplin
|
||||
- Archaludon
|
||||
- Hydrapple
|
||||
|
||||
#### Regional Forms
|
||||
- Sandshrew (Alolan Form)
|
||||
- Sandslash (Alolan Form)
|
||||
- Yamask (Galarian Form)
|
||||
- Avalugg (Hisuian Form)
|
||||
|
||||
### Model updates for the following Pokémon
|
||||
|
||||
- Sandshrew
|
||||
- Sandslash
|
||||
- Nidoking
|
||||
- Zubat
|
||||
- Golbat
|
||||
- Crobat
|
||||
- Ditto
|
||||
- Porygon
|
||||
- Porygon2
|
||||
- Porygon-Z
|
||||
- Skarmory
|
||||
- Smoochum
|
||||
- Jynx
|
||||
- Popplio
|
||||
- Brionne
|
||||
- Primarina
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Sableye
|
||||
- Mawile
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Nacli
|
||||
- Naclstack
|
||||
- Garganacl
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Tyranitar
|
||||
- Turtonator
|
||||
- Golett
|
||||
- Corsola
|
||||
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
|
||||
- ... and Substitute
|
||||
|
||||
#### Model changes to the regional bias resource pack:
|
||||
- Added Mime Jr. (Galar Bias)
|
||||
- Added Bergmite (Hisui Bias)
|
||||
- Changed models for every existing regional bias
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
|
||||
- Kakuna
|
||||
- Grimer
|
||||
- Nidoran♀ (+ New shoulder mount)
|
||||
- Nidorina
|
||||
- Nidoran♂ (+ New shoulder mount)
|
||||
- Sandshrew
|
||||
- Diglett
|
||||
- Dugtrio
|
||||
- Graveler
|
||||
- Happiny
|
||||
- Chansey
|
||||
- Blissey
|
||||
- Poliwhirl
|
||||
- Poliwrath
|
||||
- Zubat
|
||||
- Vulpix
|
||||
- Drowzee
|
||||
- Hypno
|
||||
- Noctowl
|
||||
- Skarmory
|
||||
- Skitty
|
||||
- Flygon
|
||||
- Shellos
|
||||
- Riolu
|
||||
- Lucario
|
||||
- Purrloin
|
||||
- Liepard
|
||||
- Joltik
|
||||
- Galvantula
|
||||
- Nidoking
|
||||
- Fletchling
|
||||
- Fletchinder
|
||||
- Talonflame
|
||||
- Archeops
|
||||
- Drampa
|
||||
- Sewaddle
|
||||
- Swadloon
|
||||
- Leavanny
|
||||
- Goomy
|
||||
- Sliggoo
|
||||
- Goodra
|
||||
- Shedinja
|
||||
- Skwovet
|
||||
- Greedent
|
||||
- Bidoof
|
||||
- Cranidos
|
||||
- Chatot
|
||||
- Yamask
|
||||
- Cofagrigus
|
||||
- Luvdisc
|
||||
- Staryu
|
||||
- Starmie
|
||||
- Togetic
|
||||
- Rellor
|
||||
- Rabsca
|
||||
- Yungoos
|
||||
- Gumshoos
|
||||
- Cottonee
|
||||
- Whimsicott
|
||||
- Corsola
|
||||
|
||||
#### Added sounds to the following moves
|
||||
|
||||
- Absorb
|
||||
- Aurora Beam
|
||||
- Double Team
|
||||
- Draco Meteor
|
||||
- Fire Punch
|
||||
- Fire Spin
|
||||
- Flame Charge
|
||||
- Flame Wheel
|
||||
- Giga Drain
|
||||
- Ice Punch
|
||||
- Leech Life
|
||||
- Magical Leaf
|
||||
- Mega Drain
|
||||
- Minimize
|
||||
- Poison Gas
|
||||
- Rock Throw
|
||||
- Scary Face
|
||||
- Seed bomb
|
||||
- Smokescreen
|
||||
- String Shot
|
||||
- Tail Whip
|
||||
|
||||
### Changes
|
||||
- Removed the penalty for block destruction speed when on a hover mount, making them better for digging.
|
||||
- Golden Apples can now be crafted using the Applin Apples.
|
||||
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes.
|
||||
- Updated Pokédex Screens item tag to support tagged variants of Redstone and Glowstone Dust.
|
||||
- Sharp Beak can now be crafted using bones and Flying Gems.
|
||||
- Metal Alloy can now be crafted using iron and Thunder Stones.
|
||||
- Ability Capsules can now be brewed from Medicinal Brew and Dragon Breath.
|
||||
- Using an Ability Capsule will now return an Empty Bottle.
|
||||
- Decreased the default pastured wander radius to 32 blocks.
|
||||
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release.
|
||||
- Rooted Arch Ruins, Crumbling Arch Ruins, and all fossil structures have been improved so that they generate more naturally in terrain.
|
||||
- Gimmighoul towers now generate at ground-level rather than sinking one block into the ground.
|
||||
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
|
||||
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
|
||||
- The Prehistoric Underwater Fissure and Prehistoric Submerged Impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
|
||||
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
|
||||
- Shipwreck coves now generate slightly more frequently.
|
||||
- Added equipment loot tables for shipwreck cove enemies.
|
||||
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves more easily.
|
||||
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
|
||||
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma Cove gets Blast Burn, Lush Cove gets Frenzy Plant, and Submerged Cove gets Hydro Cannon.)
|
||||
- The following items can now be obtained by brushing the following Pokémon:
|
||||
- Pink Wool - Wigglytuff
|
||||
- White Wool - Swablu, Altaria
|
||||
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
|
||||
- Phantom Membrane - Dreepy, Drakloak, Dragapult
|
||||
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
|
||||
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
|
||||
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
|
||||
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples.
|
||||
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame.
|
||||
- Refactored all advancements, removing several deemed unnecessary. Due to this cleanup, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). This sucks, and we're sorry. We had to do this eventually so better now than later.
|
||||
- Forces between colliding hitboxes now factors in the weight of Pokémon.
|
||||
- Improved berry tree performance on singleplayer and servers.
|
||||
- Updated stat increase and decrease particle effects with the addition of alpha boost particles.
|
||||
- Improved the performance of pasture blocks on singleplayer and servers.
|
||||
- Changed the bait effects of the following items: (Vanilla) Sweet & Glow Berries, Golden & Enchanted Golden Apples (Cobblemon) Custap, Eggant, Hopo & Micle Berries, Sweet Hearts.
|
||||
- Wild Pokémon now reliably have at least one offensive move if they can have offensive moves at that level. No more free lunches with Pokémon that can't even hurt you.
|
||||
- Data Monitor can accept Disks (TMs, Music Disks, Upgrade, Dubious Disk, etc.) to change what's being displayed on the screen.
|
||||
- The party overlay's experience-gained animation now displays the amount of EXP the Pokémon gained.
|
||||
- Pressing the inventory key now closes the Pokémon interact wheel GUI.
|
||||
- Updated the sound for harvesting Apricorns.
|
||||
- Lowered the volume of harvesting berries.
|
||||
- Improved compatibility with various mods
|
||||
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests.
|
||||
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks.
|
||||
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones.
|
||||
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks.
|
||||
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used.
|
||||
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles.
|
||||
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart.
|
||||
- Amendments allows for cooking Leek and Potato Stew in a Cauldron.
|
||||
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments).
|
||||
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy.
|
||||
- Berry Trees are now compatible with the Planter properly
|
||||
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues.
|
||||
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items.
|
||||
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets.
|
||||
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create.
|
||||
- Rage Candy Bar has a Create Mixing recipe.
|
||||
- All Cobblemon ores can now be crushed in Create's Crushing Wheels. All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems.
|
||||
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed.
|
||||
- Big Roots and Revival Herbs can be milled to their respective powders.
|
||||
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops.
|
||||
- Auspicious Armour and Upgrades can be Haunted using Create's crafting systems.
|
||||
- Applin Apples can be used to make Honeyed Apples in Create.
|
||||
- Sounds for interacting with various Cobblemon blocks now play from the centre of the block, rather than the corner. You probably didn't notice.
|
||||
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot.
|
||||
- Adjusted the walk speed for Croagunk and Aerodactyl.
|
||||
- The `/pc` command no longer plays PC activation sounds
|
||||
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens.
|
||||
- Ice type Pokémon are now immune to Packed Snow.
|
||||
- Added cobweb block slowness immunity for spider Pokémon
|
||||
|
||||
### Fixes
|
||||
- Fixed rare Pokémon being far too likely to spawn multiple times in a single spawn cycle. The dev told me that the exact bug is too complicated to explain.
|
||||
- Fixed Omanyte and Omastar incorrectly using water animations on land.
|
||||
- Fixed Pokédex layout for genderless variations.
|
||||
- Fixed odd stacking behaviour with Poké Baits.
|
||||
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
|
||||
- Fixed improper validation of Pokémon when performing a trade.
|
||||
- Fixed an issue where particles like beams can disappear when you don't see the beginning of the beam, or you look away.
|
||||
- Fixed Bedrock particle materials sometimes being incorrectly invisible from some sides.
|
||||
- Fixed an occasional crash on retrieving revived fossils.
|
||||
- Fixed brushing not working on Neoforge. Again.
|
||||
- Fixed braking not working for submarine-style mounts (whoops).
|
||||
- Fixed strafing not having the same friction as forward movement (funny whoops).
|
||||
- Fixed submarine-style mounts' surface-water animations not playing when supposed to.
|
||||
- Fixed submarine-style mounts' camera freaking out when looking up or down on the surface.
|
||||
- Fixed pastured Pokémon despawning and running away.
|
||||
- Fixed Apricorn trees crashing when other mods bonemeal them.
|
||||
- Fixed the issue that caused hover-style mounts without a ground style to be rendered useless upon touching the ground on a server.
|
||||
- Fixed horizontal and vertical collisions with hover-style mounts so they no longer accumulate or retain momentum.
|
||||
- Fixed NeoForge brewing stand unexpectedly crashing users when shift clicking items within its menu. Well, I suppose all crashes are unexpected. You know what I mean.
|
||||
- Fixed NPCs switching Pokémon very often during battles.
|
||||
- Fixed Campfire Pot causing crashes under certain circumstances.
|
||||
- Fixed Type Gems not being consumed upon use in battles.
|
||||
- Fixed several Ancient Pokéball's not using the correct capture multiplier.
|
||||
- Fixed a server warning that could occur when recalling Pokémon.
|
||||
- Fixed sliding particles incorrectly handling block collision.
|
||||
- Fixed Full Heal not curing status conditions in battle. Literally it's one job.
|
||||
- Fixed a rare issue with battles locking up when you are forced to switch, with the switch menu appearing instantly.
|
||||
- Fixed the combined effects of Pledge moves having broken Showdown message handling and localisation.
|
||||
- Fixed a very niche issue where structures would spawn in an incorrect location, creating very funky world gen.
|
||||
- Fixed Galarian Ponyta missing its cry.
|
||||
- Fixed various log warnings caused by redundant files and sound events.
|
||||
- Traded Pokémon will now retain the original friendship level when traded back to its OT.
|
||||
- Tumblestone Clusters now display correctly on maps.
|
||||
- Fixed removing mulch from berry trees with a shovel not working. That was meant to be a thing.
|
||||
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full.
|
||||
- Fixed Stripped Saccharine Wood recipe being... not there.
|
||||
- Fixed some performance issues with a kind of sound event causing lag by attempting to create sounds every tick when out of range of hearing.
|
||||
- Fixed stat-reducing Aprijuice not being possible to apply on a maxed out Pokémon. Also fixed the possibility of going into negatives.
|
||||
- Fixed exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so.
|
||||
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models.
|
||||
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option.
|
||||
- Fixed gilded chests being more accessible than intended.
|
||||
- Fixed fished up Pokémon not being added as "seen" to the Pokédex.
|
||||
- Fixed surprised Gimmighoul encounters not being added as "seen" to the Pokédex.
|
||||
- Fixed a crash involving berry trees that have been world edited.
|
||||
- Fixed visual desync in multiplayer for players mounting Pokémon outside other players' loaded chunks.
|
||||
- Fixed Pokémon entities leveled by rare candy not getting stronger as Minecraft entities unless recalled.
|
||||
- Fixed a freeze during game load that occurred when multiple mods tried to load Cobblemon API classes at the same time.
|
||||
- Lowered the volume of Blitzle and Zebstrika's cry (shut up!).
|
||||
- Regional Foods will now prioritise being eaten instead of used for healing Pokémon - shift clicking will use it as healing.
|
||||
- Resolved z-fighting (texture flickering) on Pokédex Models.
|
||||
- Restored Ninjask's ability to do barrel rolls.
|
||||
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
|
||||
- Fixed a crash caused by adding enchanted items to the Resurrection Machine.
|
||||
- Fixed a crash that could occur when starting a PvN battle with only fainted Pokémon.
|
||||
- Fixed the Remember Riding Camera config not being respected when dismounting and resolved the incompatibility with the Auto Third Person mod.
|
||||
- Fixed Full Heal block not dropping Full Heals. Lmao.
|
||||
- Fixed status cure items not returning an empty bottle when used.
|
||||
- Fixed Panpour consuming its held item when evolving with a Water Stone.
|
||||
- Fixed Smoked Tail Curry returning a Wooden Bowl when consumed despite it not being included in the recipe. Free bowls!
|
||||
- Fixed `/giveallpokemon` not respecting the min and max arguments.
|
||||
- Fixed the choose starter toast not disappearing when capturing a Pokémon instead of opening the starter selection screen.
|
||||
- Fixed an issue where certain Sinister Tea seasonings were only applying as instant effects and would not apply over time.
|
||||
- Long config option labels now scroll instead of overflowing in the configuration menu.
|
||||
- Fixed some z-fighting and clipping on Dewott and Hisui-Bias Dewott.
|
||||
- Eggant Berries can now be composted properly on NeoForge.
|
||||
- Composting chances are now the same on NeoForge as on Fabric.
|
||||
- Fixed problem where furnaces which had smelted Revival Herbs into Heal Powder in older versions would glitch out on trying to get the experience out of the block.
|
||||
- Fixed the Battle AI not recognizing Surging Strikes as a multistrike move.
|
||||
- Fixed Tentacool not using its swim animation when on water surface.
|
||||
- Fixed Eevee using its left shoulder pose when in battle.
|
||||
- Fixed Magikarp and Gyarados Jump Pattern resourcepacks replacing shiny textures of addons targeting Magikarp or Gyarados.
|
||||
- Fixed Combee's pollinated texture overwriting the shiny and female textures.
|
||||
- Fixed a colour mistake with Dracovish's shiny.
|
||||
- Fixed Togedemaru's back spikes z-fighting.
|
||||
- Fixed Sliggoo (+ Hisuian) evolving in the Nether and End where it can't actually rain.
|
||||
- Corrected the scales, hitbox sizes, and eye heights for Ariados, Cetoddle, Durant, Galvantula, Klink, Lanturn, Lumineon, Mantine, Mantyke, Mudbray, Numel, Staravia, and Torkoal.
|
||||
- Fixed Torkoal's walk speed not matching its animation. He will no longer move faster than Bunnelby and Buneary. This tortoise would've won.
|
||||
- Fixed Lanturn T-posing due to missing ground walk and sleep animations.
|
||||
- Fixed Oshawott and Dewott using their sleep animation in unintended scenarios.
|
||||
- Fixed Dudunsparce and Maushold always having the same form instead of having the 1 in 100 chance of being different.
|
||||
- Fixed bird mounts rapidly switching between their flying and gliding animations when flying at low speed.
|
||||
- Fixed some battles being startable with fainted Pokémon in the initial sendout.
|
||||
- Fixed random gender selection for forms with strict gender divides (such as Meowstic and Oinkologne) never choosing female (that's sexist).
|
||||
- Fixed the "Press R to battle" prompt not disappearing after you finish your first battle, at least until you relogged.
|
||||
- Fixed the composter block being listed as "composer" in the cosmetic items species feature
|
||||
- Fixed Castform's forms being tagged as introduced in generation 4 instead of 3.
|
||||
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`.
|
||||
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly.
|
||||
- Fixed Litleo not blinking.
|
||||
|
||||
### Developer
|
||||
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters.
|
||||
- Added `FishingSpawnerFactory` which allows influence registration on fishing encounters.
|
||||
- Added `BattleFleeAttemptEvent` fired from `PokemonBattle.checkFleeAttempt`, allowing developers to intercept and control flee attempts.
|
||||
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
|
||||
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
|
||||
- Separated all the functions of MoLangFunctions.kt into their own files.
|
||||
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
|
||||
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
|
||||
- Fixed nicknames not being applied to Pokémon spawned from `spawn_pool_world` files.
|
||||
- Added support for `profilePokedexScale`, `profilePokedexTranslation`, `profileSummaryScale`, and `profileSummaryTranslation` in poser files for finer control over profile displays.
|
||||
- Fixed riding camera offsets defined on Pokémon forms not being applied.
|
||||
- Marks can now provide aspects.
|
||||
|
||||
### Molang & Datapacks
|
||||
- Added `moveset_builders` datapack folder for different styles of movesets.
|
||||
- Added `party_pools` datapack folder for defining groups of Pokémon for complex NPC party definitions.
|
||||
- Added `party_compositions` datapack folder for arranging parties on NPCs that rely on party pools.
|
||||
- Added `composed_pool` NPC party provider type which combines a party pool with a party composition to create more complex NPC parties.
|
||||
- Added basic chatter NPC Behaviour.
|
||||
- Added `q.item.is_enchanted()`.
|
||||
- Added `q.item.has_enchantment(minecraft:sharpness, 3)`.
|
||||
- Added `has_chosen_starter` and `get_starter_uuid` as available Molang functions.
|
||||
- Added `has_tm_move_unlocked`, `unlock_tm_move`, and `lock_tm_move` as available Molang functions for Players.
|
||||
- Added `marks`, `has_mark`, and `remove_marks` as available Molang functions for Pokémon.
|
||||
- Added Data Component support for species drops, eg `"components": {"minecraft:custom_model_data": 123}`
|
||||
- Added the new `sortOrder` field to mark entries, (defaults to 0 if not specified) which controls the order in which marks are rendered on the HUD, with higher numbers being rendered on top of lower numbers.
|
||||
- Added optional `order` field to starter categories. It will allow explicitly sorting categories in the starter selection screen.
|
||||
- SpeciesAdditions append vs override now matches the wiki documentation.
|
||||
- Added `weighted_choice` as a new feature type. It requires setting weights for each choice and allows developers to simplify the process of assigning random aspect choice based on marble chance.
|
||||
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. Use type: `minecraft:chest` for the loot table.
|
||||
- Added `q.pokemon.entity` to access the entity of Pokémon.
|
||||
- Flamethrower and Seismic Toss had their action effect JSON cleaned up.
|
||||
- Ride sounds can now be set as exclusive to passengers.
|
||||
- Ride sounds no longer play when submerged unless specified with a new setting.
|
||||
- NPC Trainers can now call a `command` animation when issuing a command in battle.
|
||||
- Added a q.failed function to move action effects.
|
||||
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a pre-offset clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
|
||||
- `q.sound` now accepts three parameters (sound event, volume, and pitch) instead of just the sound event and pitch.
|
||||
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
|
||||
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
|
||||
|
||||
### Particles
|
||||
|
||||
#### New Move Animations
|
||||
- Blizzard
|
||||
- Disarming Voice
|
||||
- Draco Meteor
|
||||
- Dragon Breath
|
||||
- Dragon Rage
|
||||
- Drain Punch
|
||||
- Fairy Wind
|
||||
- Fire Fang
|
||||
- Gust
|
||||
- Horn Drill
|
||||
- Hydro Pump
|
||||
- Ice Fang
|
||||
- Leafage
|
||||
- Leaf Storm
|
||||
- Light Screen
|
||||
- Night Shade
|
||||
- Play Rough
|
||||
- Pyro Ball
|
||||
- Reflect
|
||||
- Rock Slide
|
||||
- Sonic Boom
|
||||
- Sucker Punch
|
||||
- Supersonic
|
||||
- Thunder Fang
|
||||
|
||||
#### Tweaks and Fixes (Moves)
|
||||
|
||||
- Confusion
|
||||
- Dragon Claw
|
||||
- Eruption
|
||||
- Fire Punch
|
||||
- Flame Burst
|
||||
- Flame Charge
|
||||
- Flamethrower
|
||||
- Frost Breath
|
||||
- Hypnosis
|
||||
- Ice Punch
|
||||
- Icy Wind
|
||||
- Mud Sport
|
||||
- Poison Fang
|
||||
- Protect
|
||||
- Psychic
|
||||
- Quick Attack
|
||||
- Rage Powder
|
||||
- Razor Leaf
|
||||
- Sand Attack
|
||||
- Scratch
|
||||
- Shadow Claw
|
||||
- Sing
|
||||
- Spore
|
||||
- String Shot
|
||||
- Tackle
|
||||
- Tail Whip
|
||||
- Thunder
|
||||
- Thunder Punch
|
||||
- Thunder Wave
|
||||
- Water Gun
|
||||
- Water Pulse
|
||||
- Water Sport
|
||||
|
||||
#### Miscellaneous Particles
|
||||
|
||||
- Added Wild Might particles for Alpha Pokémon.
|
||||
- Updated stat increase and decrease particles.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Japanese
|
||||
- German
|
||||
- Spanish
|
||||
- Ecuadorian Spanish
|
||||
- Portuguese
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Traditional Chinese
|
||||
- Korean
|
||||
- Russian
|
||||
@@ -0,0 +1,41 @@
|
||||
# Changelog
|
||||
## [1.8.1 (September 13th, 2026)](#1-8-1)
|
||||
|
||||
### Animation updates for the following Pokémon
|
||||
- Flapple, Appletun, Dipplin, Hydrapple (placeholder animations only).
|
||||
|
||||
### Fixes
|
||||
- Fixed a potential world generation stall caused by Type Gem clusters.
|
||||
- Fixed Pokémon on shoulders not animating if they are visibly holding a held item.
|
||||
- Fixed NPCs getting their own TM progress file.
|
||||
- Fixed odd NeoForge crash due to a dependency being outdated. It will now correctly flag the dependency for being outdated.
|
||||
- Fixed the TM Machine refusing move selections that the `unlockAllMoveDexMovesByDefault` config had made visible, leaving the move slot blank and the craft button unresponsive.
|
||||
- Fixed the Beast Ball not applying its 0.1× penalty to Pokémon that are not Ultra Beasts.
|
||||
- Fixed the Moon Ball reading the world's total elapsed time instead of the time of day, so its bonus applied during the day instead.
|
||||
- Fixed the Love Ball never awarding its 2.5× opposite gender bonus, and matching species by display name instead of identifier.
|
||||
- Fixed the Timer Ball being weaker than a regular Poké Ball on early turns and only reaching 4× on turn 14 instead of turn 10.
|
||||
- Fixed the Luxury Ball only doubling Friendship gains of 1, leaving level up and berry gains unaffected.
|
||||
- Fixed Rufflet, Braviary, and Hisuian Braviary flooding logs with errors about missing sound events.
|
||||
- Fixed Pokémon property typos in Pangoro and Dedenne herd spawn files.
|
||||
- Fixed unintentional watermelon cosmetic for Hisuian Electrode.
|
||||
- Fixed Blastoise having an unintentional passenger seat.
|
||||
- Fixed form typo in Rockruff-Dusk spawn, preventing it from being able to spawn.
|
||||
- Fixed the `stat_compare` and `stat_equal` evolution requirements crashing the game when given `special_attack` or `special_defence`, and made them treat any non-permanent stat as a failed requirement instead of crashing.
|
||||
- Fixed Rowap and Jacoba berries not correctly altering Pokémon sizes when used as bait.
|
||||
- Fixed Ampharos' eyes not emitting particles.
|
||||
- Fixed Alolan Raticate's cry not playing.
|
||||
- Fixed several incorrect light-based spawning conditions for ghosts and bugs.
|
||||
- Removed unused, placeholder habitat pools.
|
||||
- Fixed Mantyke, Mantine, and Quaxly getting stuck in a recoil 'expression' during battles due to having an expression assigned as a recoil animation.
|
||||
- Fixed Lucario's aura particles not emitting.
|
||||
- Fixed Lycanroc's head movement.
|
||||
- Added head movement to Archaludon.
|
||||
- Fixed Duraludon line not having their sleep animations.
|
||||
- Fixed Diglett line not having their faint animations.
|
||||
- Fixed Kingambit moving its legs while walking.
|
||||
|
||||
### Localization
|
||||
- Updated translations for:
|
||||
- Brazilian Portuguese
|
||||
- Simplified Chinese
|
||||
- Japanese
|
||||
+15
-6
@@ -1,9 +1,8 @@
|
||||
|
||||
import utilities.isSnapshot
|
||||
import utilities.version
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import utilities.gitBranch
|
||||
import utilities.gitCommit
|
||||
|
||||
/*
|
||||
*
|
||||
@@ -72,6 +71,14 @@ tasks.withType<Test> {
|
||||
}
|
||||
}
|
||||
|
||||
// Git info lives in its own output dir (not processResources) so a new commit only rewrites this one file.
|
||||
val writeGitInfo by tasks.registering(WriteProperties::class) {
|
||||
destinationFile = layout.buildDirectory.file("generated/gitInfo/cobblemon-git.properties")
|
||||
property("commit", gitCommit())
|
||||
property("branch", gitBranch())
|
||||
}
|
||||
sourceSets.main { output.dir(mapOf("builtBy" to writeGitInfo), layout.buildDirectory.dir("generated/gitInfo")) }
|
||||
|
||||
tasks.processResources {
|
||||
exclude("data/**/README.md")
|
||||
}
|
||||
@@ -98,11 +105,13 @@ sourceSets {
|
||||
property("modid", "cobblemon")
|
||||
property("version", project.version())
|
||||
property("isSnapshot", if(rootProject.isSnapshot()) "true" else "false")
|
||||
property("gitCommit", versioning.info.commit)
|
||||
property("branch", versioning.info.branch)
|
||||
System.getProperty("buildNumber")?.let { property("buildNumber", it) }
|
||||
property("timestamp", OffsetDateTime.now(ZoneOffset.UTC).format(DateTimeFormatter.ofPattern("MM/dd/yyyy hh:mm:ss")) + " UTC")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The platform jars are built from :common:jar, so the remapped common jars are only needed by `publish`
|
||||
tasks.assemble {
|
||||
setDependsOn(dependsOn.filterNot { (it as? TaskProvider<*>)?.name in setOf("remapJar", "remapSourcesJar") })
|
||||
}
|
||||
|
||||
@@ -10,11 +10,13 @@ package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.player.MountedPlayerRenderer;
|
||||
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
|
||||
import com.cobblemon.mod.common.duck.RidePassenger;
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
|
||||
import net.minecraft.client.model.HumanoidModel;
|
||||
import net.minecraft.client.model.geom.ModelPart;
|
||||
import net.minecraft.client.player.AbstractClientPlayer;
|
||||
import net.minecraft.util.Mth;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import org.spongepowered.asm.mixin.Final;
|
||||
@@ -96,4 +98,14 @@ public class HumanoidModelMixin {
|
||||
MountedPlayerRenderer.INSTANCE.animate((HumanoidModel<AbstractClientPlayer>)(Object)this, pokemonEntity, player, netHeadYaw, headPitch, ageInTicks, limbSwing, limbSwingAmount);
|
||||
}
|
||||
}
|
||||
|
||||
@Inject(method = "setupAnim*", at = @At("TAIL"))
|
||||
public void cobblemon$poseRecallArm(LivingEntity entity, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch, CallbackInfo ci) {
|
||||
if (!(entity instanceof AbstractClientPlayer) || entity.getVehicle() instanceof PokemonEntity) return;
|
||||
float raise = RecallArmAnimationTracker.INSTANCE.raiseFactor(entity.getUUID());
|
||||
if (raise <= 0f) return;
|
||||
this.rightArm.xRot = Mth.lerp(raise, this.rightArm.xRot, (float) (-Math.PI / 2.0));
|
||||
this.rightArm.yRot = Mth.lerp(raise, this.rightArm.yRot, 0f);
|
||||
this.rightArm.zRot = Mth.lerp(raise, this.rightArm.zRot, 0f);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import net.minecraft.client.renderer.entity.layers.ItemInHandLayer;
|
||||
import net.minecraft.world.entity.HumanoidArm;
|
||||
import net.minecraft.world.entity.LivingEntity;
|
||||
import net.minecraft.world.item.ItemDisplayContext;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import org.spongepowered.asm.mixin.Mixin;
|
||||
import org.spongepowered.asm.mixin.injection.At;
|
||||
import org.spongepowered.asm.mixin.injection.Inject;
|
||||
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
|
||||
|
||||
@Mixin(ItemInHandLayer.class)
|
||||
public class ItemInHandLayerMixin {
|
||||
@Inject(method = "renderArmWithItem", at = @At("HEAD"), cancellable = true)
|
||||
private void cobblemon$hideRecallHandItem(LivingEntity entity, ItemStack itemStack, ItemDisplayContext itemDisplayContext, HumanoidArm humanoidArm, PoseStack poseStack, MultiBufferSource multiBufferSource, int light, CallbackInfo ci) {
|
||||
if (humanoidArm == HumanoidArm.RIGHT && RecallArmAnimationTracker.INSTANCE.isRecalling(entity.getUUID())) {
|
||||
ci.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.mixin.client;
|
||||
|
||||
import com.cobblemon.mod.common.OrientationControllable;
|
||||
import com.cobblemon.mod.common.client.render.player.FirstPersonRecallBallRenderer;
|
||||
import com.cobblemon.mod.common.item.PokedexItem;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.minecraft.client.Minecraft;
|
||||
@@ -31,6 +32,9 @@ public class ItemInHandRendererMixin {
|
||||
if (abstractClientPlayer.isUsingItem() && abstractClientPlayer.getUseItem().getItem() instanceof PokedexItem) {
|
||||
ci.cancel();
|
||||
}
|
||||
else if (interactionHand == InteractionHand.MAIN_HAND && FirstPersonRecallBallRenderer.INSTANCE.render(poseStack, multiBufferSource, j, f)) {
|
||||
ci.cancel();
|
||||
}
|
||||
else if (abstractClientPlayer instanceof OrientationControllable controllable && controllable.getOrientationController().isActive()) {
|
||||
|
||||
}
|
||||
|
||||
@@ -8,8 +8,12 @@ object CobblemonBuildDetails {
|
||||
const val VERSION = "{{ version }}"
|
||||
const val SNAPSHOT = {{ isSnapshot }}
|
||||
|
||||
const val GIT_COMMIT = "{{ gitCommit | default("Not Specified") }}"
|
||||
const val BRANCH = "{{ branch | default("Not Specified") }}"
|
||||
// Read at runtime rather than templated in, so a new commit doesn't invalidate compiled code.
|
||||
private val git = java.util.Properties().apply {
|
||||
CobblemonBuildDetails::class.java.getResourceAsStream("/cobblemon-git.properties")?.use(::load)
|
||||
}
|
||||
@JvmField val GIT_COMMIT: String = git.getProperty("commit", "Not Specified")
|
||||
@JvmField val BRANCH: String = git.getProperty("branch", "Not Specified")
|
||||
|
||||
fun smallCommitHash(): String {
|
||||
return this.GIT_COMMIT.substring(0, 7)
|
||||
|
||||
@@ -16,6 +16,8 @@ import com.cobblemon.mod.common.block.MintBlock.MintType
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfireBlock
|
||||
import com.cobblemon.mod.common.block.campfirepot.CampfirePotBlock
|
||||
import com.cobblemon.mod.common.block.chest.GildedChestBlock
|
||||
import com.cobblemon.mod.common.block.decor.ActivatableDecorationBlock
|
||||
import com.cobblemon.mod.common.block.decor.RingTargetBlock
|
||||
import com.cobblemon.mod.common.block.general.BaleBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationCarpetBlock
|
||||
import com.cobblemon.mod.common.block.general.HorizontalRotationalBlock
|
||||
@@ -621,37 +623,119 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
val WEAKNESS_POLICY = create("weakness_policy", WallAttachedDirectionalShapeBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_PAPER_LARGE_SOUNDS).mapColor(MapColor.NONE).noOcclusion().noCollission(), 14, 12, 1))
|
||||
|
||||
@JvmField
|
||||
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
val RING_TARGET = create("ring_target", RingTargetBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOD).mapColor(MapColor.NONE).noOcclusion().strength(0.125f)))
|
||||
@JvmField
|
||||
val ANTIDOTE = create("antidote", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
val MAGNET = create("magnet", MagnetBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion().strength(0.075f)))
|
||||
@JvmField
|
||||
val AWAKENING = create("awakening", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val BURN_HEAL = create("burn_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ICE_HEAL = create("ice_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val PARALYZE_HEAL = create("paralyze_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
val GALARICA_WREATH = create("galarica_wreath", GalaricaWreathBlock(BlockBehaviour.Properties.of().sound(SoundType.AZALEA_LEAVES).mapColor(MapColor.NONE).noOcclusion().noCollission()))
|
||||
|
||||
@JvmField
|
||||
val ETHER = create("ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
val FLAME_ORB = create("flame_orb", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().lightLevel{15}))
|
||||
@JvmField
|
||||
val MAX_ETHER = create("max_ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
val TOXIC_ORB = create("toxic_orb", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion()))
|
||||
@JvmField
|
||||
val ELIXIR = create("elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
val LIFE_ORB = create("life_orb", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().lightLevel{7}))
|
||||
@JvmField
|
||||
val MAX_ELIXIR = create("max_elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
val LIGHT_BALL = create("light_ball", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().lightLevel{13}))
|
||||
@JvmField
|
||||
val SMOKE_BALL = create("smoke_ball", OrbBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion()))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
val KINGS_ROCK = create("kings_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.CENTERED_6x))
|
||||
|
||||
@JvmField
|
||||
val SUPER_POTION = create("super_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
val GALARICA_CUFF = create("galarica_cuff", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.AZALEA_LEAVES).mapColor(MapColor.NONE).noCollission().noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_10x4))
|
||||
@JvmField
|
||||
val HYPER_POTION = create("hyper_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
val UNREMARKABLE_TEACUP = create("unremarkable_teacup", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEACUP))
|
||||
@JvmField
|
||||
val MAX_POTION = create("max_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
val MASTERPIECE_TEACUP = create("masterpiece_teacup", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEACUP))
|
||||
@JvmField
|
||||
val FULL_RESTORE = create("full_restore", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
val CHIPPED_POT = create("chipped_pot", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEAPOT))
|
||||
@JvmField
|
||||
val CRACKED_POT = create("cracked_pot", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DECORATED_POT).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.TEAPOT))
|
||||
|
||||
@JvmField
|
||||
val SACHET = create("sachet", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOL).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val METAL_POWDER = create("metal_powder", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val QUICK_POWDER = create("quick_powder", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
@JvmField
|
||||
val BRIGHT_POWDER = create("bright_powder", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_POWDER_SOUNDS).mapColor(MapColor.NONE).noOcclusion().lightLevel{3}, DecorativeItemBlock.DecorativeItemBlockType.POUCH))
|
||||
|
||||
@JvmField
|
||||
val NEVER_MELT_ICE = create("never_melt_ice", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.GLASS).mapColor(MapColor.NONE).noOcclusion().friction(0.989F), DecorativeItemBlock.DecorativeItemBlockType.NEVERMELT))
|
||||
@JvmField
|
||||
val SCOPE_LENS = create("scope_lens", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.COPPER).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.SCOPE))
|
||||
@JvmField
|
||||
val WIDE_LENS = create("wide_lens", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.COPPER).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.WIDE))
|
||||
@JvmField
|
||||
val ZOOM_LENS = create("zoom_lens", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.COPPER).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.ZOOM))
|
||||
|
||||
@JvmField
|
||||
val ICY_ROCK = create("icy_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x4))
|
||||
@JvmField
|
||||
val HEAT_ROCK = create("heat_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x4))
|
||||
@JvmField
|
||||
val SMOOTH_ROCK = create("smooth_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x4))
|
||||
@JvmField
|
||||
val DAMP_ROCK = create("damp_rock", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.DEEPSLATE).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.FLATTENED_8x6))
|
||||
@JvmField
|
||||
val DESTINY_KNOT = create("destiny_knot", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.WOOL).mapColor(MapColor.NONE).noOcclusion(), DecorativeItemBlock.DecorativeItemBlockType.KNOT))
|
||||
@JvmField
|
||||
val LUMINOUS_MOSS = create("luminous_moss", DecorativeItemBlock(BlockBehaviour.Properties.of().sound(SoundType.MOSS).mapColor(MapColor.NONE).noOcclusion().lightLevel{5}, DecorativeItemBlock.DecorativeItemBlockType.CENTERED_8x))
|
||||
|
||||
@JvmField
|
||||
val METRONOME = create("metronome", ActivatableDecorationBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion(), ActivatableDecorationBlock.ActivatableDecorationBlockType.METRONOME))
|
||||
@JvmField
|
||||
val EJECT_BUTTON = create("eject_button", EjectButtonBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).pushReaction(PushReaction.DESTROY).noOcclusion().strength(0.125f), 40))
|
||||
|
||||
@JvmField
|
||||
val LOADED_DICE = create("loaded_dice", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_DICE_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.DICE))
|
||||
@JvmField
|
||||
val IRON_BALL = create("iron_ball", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.IRON_BALL))
|
||||
@JvmField
|
||||
val METAL_COAT = create("metal_coat", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.METAL).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.METAL_COAT))
|
||||
@JvmField
|
||||
val UPGRADE = create("upgrade", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.UPGRADE))
|
||||
@JvmField
|
||||
val DUBIOUS_DISC = create("dubious_disc", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.UPGRADE))
|
||||
|
||||
@JvmField
|
||||
val THROAT_SPRAY = create("throat_spray", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.THROAT_SPRAY))
|
||||
|
||||
@JvmField
|
||||
val FULL_HEAL = create("full_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ANTIDOTE = create("antidote", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val AWAKENING = create("awakening", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val BURN_HEAL = create("burn_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val ICE_HEAL = create("ice_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
@JvmField
|
||||
val PARALYZE_HEAL = create("paralyze_heal", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.STATUS_RESTORE))
|
||||
|
||||
@JvmField
|
||||
val ETHER = create("ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val MAX_ETHER = create("max_ether", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val ELIXIR = create("elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
@JvmField
|
||||
val MAX_ELIXIR = create("max_elixir", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.ETHER))
|
||||
|
||||
@JvmField
|
||||
val POTION = create("potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val SUPER_POTION = create("super_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val HYPER_POTION = create("hyper_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.POTION))
|
||||
@JvmField
|
||||
val MAX_POTION = create("max_potion", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
@JvmField
|
||||
val FULL_RESTORE = create("full_restore", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.MAX_POTION))
|
||||
|
||||
@JvmField
|
||||
val DIRE_HIT = create("dire_hit", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
@@ -670,6 +754,23 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val X_SPEED = create("x_${Stats.SPEED.identifier.path}", StackableItemBlock(BlockBehaviour.Properties.of().sound(SoundType.CANDLE).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.BATTLE_ITEM))
|
||||
|
||||
@JvmField
|
||||
val HP_UP = create("hp_up", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val PROTEIN = create("protein", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val IRON = create("iron", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val CARBOS = create("carbos", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val CALCIUM = create("calcium", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val ZINC = create("zinc", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val PP_UP = create("pp_up", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
@JvmField
|
||||
val PP_MAX = create("pp_max", StackableItemBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.ITEM_BLOCK_BOTTLE_PILLS_SOUNDS).mapColor(MapColor.NONE).noOcclusion(), StackableItemBlock.StackableItemBlockType.VITAMIN))
|
||||
|
||||
@JvmField
|
||||
val MONITOR = create("monitor", MonitorBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).requiresCorrectToolForDrops().strength(5.0F, 6.0F).lightLevel { if (it.getValue(MonitorBlock.SCREEN) != MonitorBlock.MonitorScreen.OFF) 13 else 0 }))
|
||||
@JvmField
|
||||
@@ -685,6 +786,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
@JvmField
|
||||
val PC = create("pc", PCBlock(BlockBehaviour.Properties.of().mapColor(MapColor.METAL).sound(SoundType.METAL).pushReaction(PushReaction.BLOCK).strength(2F).noOcclusion().lightLevel { if ((it.getValue(PCBlock.ON) as Boolean) && (it.getValue(PCBlock.PART) == PCBlock.PCPart.TOP)) 13 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val TM_MACHINE = create(
|
||||
"tm_machine",
|
||||
TMMachineBlock(
|
||||
@@ -713,7 +815,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
|
||||
|
||||
@JvmField
|
||||
val PASTURE = create("pasture", PastureBlock(BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BROWN).sound(SoundType.WOOD).strength(2F).noOcclusion().pushReaction(PushReaction.BLOCK).lightLevel { if ((it.getValue(PastureBlock.ON) as Boolean) && (it.getValue(PastureBlock.PART) == PastureBlock.PasturePart.TOP)) 13 else 0 }))
|
||||
|
||||
@JvmField
|
||||
val RED_MINT = create("red_mint", MintBlock(MintType.RED, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_RED).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MINT_SOUNDS).pushReaction(PushReaction.DESTROY)))
|
||||
@JvmField
|
||||
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, BlockBehaviour.Properties.of().mapColor(MapColor.COLOR_BLUE).noCollission().randomTicks().instabreak().sound(CobblemonSounds.MINT_SOUNDS).pushReaction(PushReaction.DESTROY)))
|
||||
|
||||
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
|
||||
import com.mojang.brigadier.arguments.ArgumentType
|
||||
import kotlin.reflect.KClass
|
||||
import net.minecraft.commands.synchronization.ArgumentTypeInfo
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.resources.ResourceKey
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.MinecraftServer
|
||||
@@ -168,12 +169,16 @@ interface CobblemonImplementation {
|
||||
* TODO
|
||||
*
|
||||
* @param identifier
|
||||
* @param reloader
|
||||
* @param reloaderFactory
|
||||
* @param type
|
||||
* @param dependencies
|
||||
*/
|
||||
fun registerResourceReloader(identifier: ResourceLocation, reloader: PreparableReloadListener, type: PackType, dependencies: Collection<ResourceLocation>)
|
||||
|
||||
fun registerResourceReloader(
|
||||
identifier: ResourceLocation,
|
||||
type: PackType,
|
||||
dependencies: Collection<ResourceLocation>,
|
||||
reloaderFactory: (HolderLookup.Provider) -> PreparableReloadListener
|
||||
)
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
|
||||
@@ -472,9 +472,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
// Evolution items
|
||||
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
|
||||
@JvmField val DRAGON_SCALE = noSettingsItem("dragon_scale")
|
||||
@JvmField val METAL_COAT = noSettingsItem("metal_coat")
|
||||
@JvmField val UPGRADE = noSettingsItem("upgrade")
|
||||
@JvmField val DUBIOUS_DISC = noSettingsItem("dubious_disc")
|
||||
@JvmField val METAL_COAT = itemNameBlockItem("metal_coat", CobblemonBlocks.METAL_COAT)
|
||||
@JvmField val UPGRADE = itemNameBlockItem("upgrade", CobblemonBlocks.UPGRADE)
|
||||
@JvmField val DUBIOUS_DISC = itemNameBlockItem("dubious_disc", CobblemonBlocks.DUBIOUS_DISC)
|
||||
@JvmField val DEEP_SEA_SCALE = noSettingsItem("deep_sea_scale")
|
||||
@JvmField val DEEP_SEA_TOOTH = noSettingsItem("deep_sea_tooth")
|
||||
@JvmField val ELECTIRIZER = noSettingsItem("electirizer")
|
||||
@@ -483,7 +483,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val PROTECTOR = noSettingsItem("protector")
|
||||
@JvmField val REAPER_CLOTH = noSettingsItem("reaper_cloth")
|
||||
@JvmField val PRISM_SCALE = noSettingsItem("prism_scale")
|
||||
@JvmField val SACHET = noSettingsItem("sachet")
|
||||
@JvmField val SACHET = itemNameBlockItem("sachet", CobblemonBlocks.SACHET)
|
||||
@JvmField val WHIPPED_DREAM = create("whipped_dream", foodItem(8, 0.3f))
|
||||
@JvmField val STRAWBERRY_SWEET = create("strawberry_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val LOVE_SWEET = create("love_sweet", foodItem(6, 0.125f))
|
||||
@@ -492,22 +492,20 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val FLOWER_SWEET = create("flower_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val STAR_SWEET = create("star_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val RIBBON_SWEET = create("ribbon_sweet", foodItem(6, 0.125f))
|
||||
@JvmField val CHIPPED_POT = noSettingsItem("chipped_pot")
|
||||
@JvmField val CRACKED_POT = noSettingsItem("cracked_pot")
|
||||
@JvmField val MASTERPIECE_TEACUP = noSettingsItem("masterpiece_teacup")
|
||||
@JvmField val UNREMARKABLE_TEACUP = noSettingsItem("unremarkable_teacup")
|
||||
@JvmField val CHIPPED_POT = itemNameBlockItem("chipped_pot", CobblemonBlocks.CHIPPED_POT)
|
||||
@JvmField val CRACKED_POT = itemNameBlockItem("cracked_pot", CobblemonBlocks.CRACKED_POT)
|
||||
@JvmField val MASTERPIECE_TEACUP = itemNameBlockItem("masterpiece_teacup", CobblemonBlocks.MASTERPIECE_TEACUP)
|
||||
@JvmField val UNREMARKABLE_TEACUP = itemNameBlockItem("unremarkable_teacup", CobblemonBlocks.UNREMARKABLE_TEACUP)
|
||||
@JvmField val SWEET_APPLE = compostableItem("sweet_apple", foodItem(4, 0.3f), 0.65f)
|
||||
@JvmField val TART_APPLE = compostableItem("tart_apple", foodItem(4, 0.3f), 0.65f)
|
||||
@JvmField val SYRUPY_APPLE = compostableItem("syrupy_apple", foodItem(4, 0.3f), 0.65f)
|
||||
@JvmField val GALARICA_CUFF = noSettingsItem("galarica_cuff")
|
||||
@JvmField val GALARICA_WREATH = noSettingsItem("galarica_wreath")
|
||||
@JvmField val GALARICA_CUFF = itemNameBlockItem("galarica_cuff", CobblemonBlocks.GALARICA_CUFF)
|
||||
@JvmField val BLACK_AUGURITE = noSettingsItem("black_augurite")
|
||||
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
|
||||
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
|
||||
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
|
||||
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
|
||||
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
|
||||
@JvmField val SHELL_HELMET = heldItem("shell_helmet")
|
||||
@JvmField val METAL_ALLOY = noSettingsItem("metal_alloy")
|
||||
@JvmField val SCROLL_OF_DARKNESS = noSettingsItem("scroll_of_darkness")
|
||||
@JvmField val SCROLL_OF_WATERS = noSettingsItem("scroll_of_waters")
|
||||
@@ -651,17 +649,17 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val EXPERIENCE_CANDY_XL = candyItem("exp_candy_xl") { _, _ -> CandyItem.DEFAULT_XL_CANDY_YIELD }
|
||||
@JvmField
|
||||
val CALCIUM = create("calcium", VitaminItem(Stats.SPECIAL_ATTACK))
|
||||
val CALCIUM = create("calcium", VitaminItem(Stats.SPECIAL_ATTACK, CobblemonBlocks.CALCIUM))
|
||||
@JvmField
|
||||
val CARBOS = create("carbos", VitaminItem(Stats.SPEED))
|
||||
val CARBOS = create("carbos", VitaminItem(Stats.SPEED, CobblemonBlocks.CARBOS))
|
||||
@JvmField
|
||||
val HP_UP = create("hp_up", VitaminItem(Stats.HP))
|
||||
val HP_UP = create("hp_up", VitaminItem(Stats.HP, CobblemonBlocks.HP_UP))
|
||||
@JvmField
|
||||
val IRON = create("iron", VitaminItem(Stats.DEFENCE))
|
||||
val IRON = create("iron", VitaminItem(Stats.DEFENCE, CobblemonBlocks.IRON))
|
||||
@JvmField
|
||||
val PROTEIN = create("protein", VitaminItem(Stats.ATTACK))
|
||||
val PROTEIN = create("protein", VitaminItem(Stats.ATTACK, CobblemonBlocks.PROTEIN))
|
||||
@JvmField
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
|
||||
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE, CobblemonBlocks.ZINC))
|
||||
@JvmField
|
||||
val HEALTH_MOCHI = create("health_mochi", MochiItem(Stats.HP))
|
||||
@JvmField
|
||||
@@ -743,9 +741,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val MAX_REVIVE = create("max_revive", ReviveItem(max = true))
|
||||
@JvmField
|
||||
val PP_UP = create("pp_up", PPUpItem(1))
|
||||
val PP_UP = create("pp_up", PPUpItem(1, CobblemonBlocks.PP_UP))
|
||||
@JvmField
|
||||
val PP_MAX = create("pp_max", PPUpItem(3))
|
||||
val PP_MAX = create("pp_max", PPUpItem(3, CobblemonBlocks.PP_MAX))
|
||||
|
||||
@JvmField
|
||||
val RED_MINT_SEEDS = mintSeed("red", MintType.RED.getCropBlock())
|
||||
@@ -922,11 +920,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField val CHOICE_SPECS = wearableItem("choice_specs")
|
||||
@JvmField val EXP_SHARE = wearableItem("exp_share")
|
||||
@JvmField val FOCUS_BAND = wearableItem("focus_band")
|
||||
@JvmField val KINGS_ROCK = wearableItem("kings_rock")
|
||||
@JvmField val KINGS_ROCK = wearableBlockItem("kings_rock", CobblemonBlocks.KINGS_ROCK)
|
||||
@JvmField val NEVER_MELT_ICE = wearableBlockItem("never_melt_ice", CobblemonBlocks.NEVER_MELT_ICE)
|
||||
@JvmField val MUSCLE_BAND = wearableItem("muscle_band")
|
||||
@JvmField val ROCKY_HELMET = wearableItem("rocky_helmet")
|
||||
@JvmField val SAFETY_GOGGLES = wearableItem("safety_goggles")
|
||||
@JvmField val WISE_GLASSES = wearableItem("wise_glasses")
|
||||
@JvmField val SHARP_BEAK = wearableItem("sharp_beak")
|
||||
@JvmField val COVERT_CLOAK = wearableItem("covert_cloak")
|
||||
@JvmField val SHELL_HELMET = wearableItem("shell_helmet")
|
||||
@JvmField val GALARICA_WREATH = wearableBlockItem("galarica_wreath", CobblemonBlocks.GALARICA_WREATH)
|
||||
@JvmField val CLEANSE_TAG = wearableBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
|
||||
@JvmField val SPELL_TAG = wearableBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
|
||||
|
||||
// Held Items
|
||||
@JvmField
|
||||
@@ -954,17 +959,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val CHOICE_SCARF = heldItem("choice_scarf")
|
||||
@JvmField
|
||||
val CLEANSE_TAG = itemNameBlockItem("cleanse_tag", CobblemonBlocks.CLEANSE_TAG)
|
||||
@JvmField
|
||||
val CLEAR_AMULET = heldItem("clear_amulet")
|
||||
@JvmField
|
||||
val COVERT_CLOAK = heldItem("covert_cloak")
|
||||
@JvmField
|
||||
val DESTINY_KNOT = heldItem("destiny_knot")
|
||||
val DESTINY_KNOT = itemNameBlockItem("destiny_knot", CobblemonBlocks.DESTINY_KNOT)
|
||||
@JvmField
|
||||
val DRAGON_FANG = heldItem("dragon_fang")
|
||||
@JvmField
|
||||
val EJECT_BUTTON = heldItem("eject_button")
|
||||
val EJECT_BUTTON = itemNameBlockItem("eject_button", CobblemonBlocks.EJECT_BUTTON)
|
||||
@JvmField
|
||||
val EJECT_PACK = heldItem("eject_pack")
|
||||
@JvmField
|
||||
@@ -978,7 +979,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val FAIRY_FEATHER = heldItem("fairy_feather")
|
||||
@JvmField
|
||||
val FLAME_ORB = heldItem("flame_orb")
|
||||
val FLAME_ORB = itemNameBlockItem("flame_orb", CobblemonBlocks.FLAME_ORB)
|
||||
@JvmField
|
||||
val FLOAT_STONE = heldItem("float_stone")
|
||||
@JvmField
|
||||
@@ -992,34 +993,32 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val HEAVY_DUTY_BOOTS = heldItem("heavy_duty_boots")
|
||||
@JvmField
|
||||
val IRON_BALL = heldItem("iron_ball")
|
||||
val IRON_BALL = itemNameBlockItem("iron_ball", CobblemonBlocks.IRON_BALL)
|
||||
@JvmField
|
||||
val LAGGING_TAIL = heldItem("lagging_tail")
|
||||
@JvmField
|
||||
val LEFTOVERS = compostableHeldItem("leftovers", null, 0.50f)
|
||||
@JvmField
|
||||
val LIFE_ORB = heldItem("life_orb")
|
||||
val LIFE_ORB = itemNameBlockItem("life_orb", CobblemonBlocks.LIFE_ORB)
|
||||
@JvmField
|
||||
val LIGHT_BALL = heldItem("light_ball")
|
||||
val LIGHT_BALL = itemNameBlockItem("light_ball", CobblemonBlocks.LIGHT_BALL)
|
||||
@JvmField
|
||||
val LIGHT_CLAY = heldItem("light_clay")
|
||||
@JvmField
|
||||
val LOADED_DICE = heldItem("loaded_dice")
|
||||
val LOADED_DICE = itemNameBlockItem("loaded_dice", CobblemonBlocks.LOADED_DICE)
|
||||
@JvmField
|
||||
val LUCKY_EGG = heldItem("lucky_egg")
|
||||
@JvmField
|
||||
val LUMINOUS_MOSS = heldItem("luminous_moss")
|
||||
val LUMINOUS_MOSS = itemNameBlockItem("luminous_moss", CobblemonBlocks.LUMINOUS_MOSS)
|
||||
@JvmField
|
||||
val MAGNET = heldItem("magnet")
|
||||
val MAGNET = itemNameBlockItem("magnet", CobblemonBlocks.MAGNET)
|
||||
@JvmField
|
||||
val METRONOME = heldItem("metronome")
|
||||
val METRONOME = itemNameBlockItem("metronome", CobblemonBlocks.METRONOME)
|
||||
@JvmField
|
||||
val MIRACLE_SEED = heldItem("miracle_seed")
|
||||
@JvmField
|
||||
val MYSTIC_WATER = heldItem("mystic_water")
|
||||
@JvmField
|
||||
val NEVER_MELT_ICE = heldItem("never_melt_ice")
|
||||
@JvmField
|
||||
val POISON_BARB = heldItem("poison_barb")
|
||||
@JvmField
|
||||
val POWER_ANKLET = heldItem("power_anklet")
|
||||
@@ -1044,13 +1043,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val RED_CARD = heldItem("red_card")
|
||||
@JvmField
|
||||
val RING_TARGET = heldItem("ring_target")
|
||||
val RING_TARGET = itemNameBlockItem("ring_target", CobblemonBlocks.RING_TARGET)
|
||||
@JvmField
|
||||
val ROOM_SERVICE = heldItem("room_service")
|
||||
@JvmField
|
||||
val SCOPE_LENS = heldItem("scope_lens")
|
||||
@JvmField
|
||||
val SHARP_BEAK = heldItem("sharp_beak")
|
||||
val SCOPE_LENS = itemNameBlockItem("scope_lens", CobblemonBlocks.SCOPE_LENS)
|
||||
@JvmField
|
||||
val SHED_SHELL = heldItem("shed_shell")
|
||||
@JvmField
|
||||
@@ -1062,9 +1059,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val SOFT_SAND = heldItem("soft_sand")
|
||||
@JvmField
|
||||
val SPELL_TAG = itemNameBlockItem("spell_tag", CobblemonBlocks.SPELL_TAG)
|
||||
@JvmField
|
||||
val SMOKE_BALL = heldItem("smoke_ball")
|
||||
val SMOKE_BALL = itemNameBlockItem("smoke_ball", CobblemonBlocks.SMOKE_BALL)
|
||||
@JvmField
|
||||
val SOOTHE_BELL = heldItem("soothe_bell")
|
||||
@JvmField
|
||||
@@ -1072,9 +1067,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val TERRAIN_EXTENDER = heldItem("terrain_extender")
|
||||
@JvmField
|
||||
val THROAT_SPRAY = heldItem("throat_spray")
|
||||
val THROAT_SPRAY = itemNameBlockItem("throat_spray", CobblemonBlocks.THROAT_SPRAY)
|
||||
@JvmField
|
||||
val TOXIC_ORB = heldItem("toxic_orb")
|
||||
val TOXIC_ORB = itemNameBlockItem("toxic_orb", CobblemonBlocks.TOXIC_ORB)
|
||||
@JvmField
|
||||
val TWISTED_SPOON = heldItem("twisted_spoon")
|
||||
@JvmField
|
||||
@@ -1082,9 +1077,9 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val WEAKNESS_POLICY = itemNameBlockItem("weakness_policy", CobblemonBlocks.WEAKNESS_POLICY)
|
||||
@JvmField
|
||||
val WIDE_LENS = heldItem("wide_lens")
|
||||
val WIDE_LENS = itemNameBlockItem("wide_lens", CobblemonBlocks.WIDE_LENS)
|
||||
@JvmField
|
||||
val ZOOM_LENS = heldItem("zoom_lens")
|
||||
val ZOOM_LENS = itemNameBlockItem("zoom_lens", CobblemonBlocks.ZOOM_LENS)
|
||||
@JvmField
|
||||
val MENTAL_HERB = compostableHeldItem("mental_herb", null, 0.85F)
|
||||
@JvmField
|
||||
@@ -1096,19 +1091,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
@JvmField
|
||||
val WHITE_HERB = compostableHeldItem("white_herb", null, 0.85F)
|
||||
@JvmField
|
||||
val BRIGHT_POWDER = heldItem("bright_powder")
|
||||
val BRIGHT_POWDER = itemNameBlockItem("bright_powder", CobblemonBlocks.BRIGHT_POWDER)
|
||||
@JvmField
|
||||
val METAL_POWDER = heldItem("metal_powder")
|
||||
val METAL_POWDER = itemNameBlockItem("metal_powder", CobblemonBlocks.METAL_POWDER)
|
||||
@JvmField
|
||||
val QUICK_POWDER = heldItem("quick_powder")
|
||||
val QUICK_POWDER = itemNameBlockItem("quick_powder", CobblemonBlocks.QUICK_POWDER)
|
||||
@JvmField
|
||||
val DAMP_ROCK = heldItem("damp_rock")
|
||||
val DAMP_ROCK = itemNameBlockItem("damp_rock", CobblemonBlocks.DAMP_ROCK)
|
||||
@JvmField
|
||||
val HEAT_ROCK = heldItem("heat_rock")
|
||||
val HEAT_ROCK = itemNameBlockItem("heat_rock", CobblemonBlocks.HEAT_ROCK)
|
||||
@JvmField
|
||||
val SMOOTH_ROCK = heldItem("smooth_rock")
|
||||
val SMOOTH_ROCK = itemNameBlockItem("smooth_rock", CobblemonBlocks.SMOOTH_ROCK)
|
||||
@JvmField
|
||||
val ICY_ROCK = heldItem("icy_rock")
|
||||
val ICY_ROCK = itemNameBlockItem("icy_rock", CobblemonBlocks.ICY_ROCK)
|
||||
|
||||
// Mulch
|
||||
@JvmField
|
||||
@@ -1599,7 +1594,16 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
|
||||
|
||||
private fun wearableItem(name: String, heldItemRemappedName: String? = null): CobblemonItem = create(
|
||||
name,
|
||||
WearableItem(name).also {
|
||||
WearableHatItem(name).also {
|
||||
wearables.add(it)
|
||||
if (heldItemRemappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, heldItemRemappedName)
|
||||
}
|
||||
}
|
||||
)
|
||||
private fun wearableBlockItem(name: String, block: Block, heldItemRemappedName: String? = null): WearableBlockItem = create(
|
||||
name,
|
||||
WearableBlockItem(name, block).also {
|
||||
wearables.add(it)
|
||||
if (heldItemRemappedName != null) {
|
||||
CobblemonHeldItemManager.registerRemap(it, heldItemRemappedName)
|
||||
|
||||
@@ -425,6 +425,7 @@ object CobblemonNetwork {
|
||||
list.add(PacketRegisterInfo(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(SpeciesEvolutionSyncPacket.ID, SpeciesEvolutionSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
|
||||
|
||||
@@ -657,5 +657,65 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
|
||||
SoundEvents.WOOL_FALL
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_PLACE = this.create("block.item_block.powder.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_BREAK = this.create("block.item_block.powder.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_STEP = this.create("block.item_block.powder.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_POWDER_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_POWDER_BREAK,
|
||||
ITEM_BLOCK_POWDER_STEP,
|
||||
ITEM_BLOCK_POWDER_PLACE,
|
||||
ITEM_BLOCK_POWDER_STEP,
|
||||
ITEM_BLOCK_POWDER_STEP
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_PLACE = this.create("block.item_block.bottle_liquid.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_BREAK = this.create("block.item_block.bottle_liquid.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_STEP = this.create("block.item_block.bottle_liquid.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_LIQUID_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_BREAK,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_STEP,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_LIQUID_PLACE
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_PLACE = this.create("block.item_block.bottle_pills.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_BREAK = this.create("block.item_block.bottle_pills.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_STEP = this.create("block.item_block.bottle_pills.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_BOTTLE_PILLS_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_BREAK,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_STEP,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_PLACE,
|
||||
ITEM_BLOCK_BOTTLE_PILLS_PLACE
|
||||
)
|
||||
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_PLACE = this.create("block.item_block.dice.place")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_BREAK = this.create("block.item_block.dice.break")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_STEP = this.create("block.item_block.dice.step")
|
||||
@JvmField
|
||||
val ITEM_BLOCK_DICE_SOUNDS = SoundType(1f, 1.1f,
|
||||
ITEM_BLOCK_DICE_BREAK,
|
||||
ITEM_BLOCK_DICE_STEP,
|
||||
ITEM_BLOCK_DICE_PLACE,
|
||||
ITEM_BLOCK_DICE_STEP,
|
||||
ITEM_BLOCK_DICE_STEP
|
||||
)
|
||||
|
||||
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.createVariableRangeEvent(cobblemonResource(name)))
|
||||
}
|
||||
|
||||
+12
-1
@@ -11,6 +11,9 @@ package com.cobblemon.mod.common.api.conditional
|
||||
import com.google.gson.JsonDeserializationContext
|
||||
import com.google.gson.JsonDeserializer
|
||||
import com.google.gson.JsonElement
|
||||
import com.google.gson.JsonPrimitive
|
||||
import com.google.gson.JsonSerializationContext
|
||||
import com.google.gson.JsonSerializer
|
||||
import java.lang.reflect.Type
|
||||
|
||||
/**
|
||||
@@ -21,11 +24,19 @@ import java.lang.reflect.Type
|
||||
* @author Hiroku
|
||||
* @since July 17th, 2022
|
||||
*/
|
||||
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>> {
|
||||
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>>, JsonSerializer<RegistryLikeCondition<B>> {
|
||||
val registryLikeConditions: MutableList<(JsonElement) -> RegistryLikeCondition<B>?>
|
||||
|
||||
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): RegistryLikeCondition<B> {
|
||||
return registryLikeConditions.firstNotNullOfOrNull { it(json) }
|
||||
?: throw IllegalArgumentException("Unable to deserialize $json")
|
||||
}
|
||||
|
||||
override fun serialize(src: RegistryLikeCondition<B>, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
|
||||
return when (src) {
|
||||
is RegistryLikeTagCondition<B> -> JsonPrimitive(RegistryLikeTagCondition.PREFIX + src.tag.location())
|
||||
is RegistryLikeIdentifierCondition<B> -> JsonPrimitive(src.identifier.toString())
|
||||
else -> throw IllegalArgumentException("Unable to serialize $src")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.api.data
|
||||
|
||||
import com.cobblemon.mod.common.util.endsWith
|
||||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.core.HolderLookup
|
||||
import java.io.BufferedReader
|
||||
import java.io.File
|
||||
import java.util.concurrent.ExecutionException
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.packs.PackType
|
||||
import net.minecraft.server.packs.resources.ResourceManager
|
||||
|
||||
/**
|
||||
* A [DataRegistry] that consumes JSON files.
|
||||
* Every deserialized instance is attached to an [Identifier].
|
||||
* For example a file under data/mymod/[resourcePath]/entry.json would be backed by the identifier modid:entry.
|
||||
*
|
||||
* @param T The type of the data consumed by this registry.
|
||||
*
|
||||
* @author Licious
|
||||
* @since August 5th, 2022
|
||||
*/
|
||||
interface ServerJsonDataRegistry<T> : DataRegistry {
|
||||
|
||||
/**
|
||||
* The [TypeToken] of type [T].
|
||||
*/
|
||||
val typeToken: TypeToken<T>
|
||||
|
||||
/**
|
||||
* The folder location for the data this registry will consume.
|
||||
*/
|
||||
val resourcePath: String
|
||||
|
||||
override val type: PackType
|
||||
get() = PackType.SERVER_DATA
|
||||
|
||||
override fun reload(manager: ResourceManager) {
|
||||
//NO-OP, handled by reload(manager, registry)
|
||||
}
|
||||
|
||||
fun reload(manager: ResourceManager, registry: HolderLookup.Provider) {
|
||||
val gson = setupParser(registry)
|
||||
val data = hashMapOf<ResourceLocation, T>()
|
||||
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
|
||||
if (identifier.namespace == "pixelmon") {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
resource.open().use { stream ->
|
||||
stream.bufferedReader().use { reader ->
|
||||
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
|
||||
data[resolvedIdentifier] = parse(reader, resolvedIdentifier, gson)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
reload(data, registry)
|
||||
}
|
||||
|
||||
fun parse(reader: BufferedReader, identifier: ResourceLocation, gson: Gson): T {
|
||||
return try {
|
||||
gson.fromJson(reader, typeToken.type)
|
||||
} catch (exception: Exception) {
|
||||
throw ExecutionException("Error loading JSON for data: $identifier", exception)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows the creation of [Gson] instances which have adapters that are registry-aware, e.g. [com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter]
|
||||
*/
|
||||
fun setupParser(registry: HolderLookup.Provider): Gson
|
||||
|
||||
/**
|
||||
* Reloads this registry from the deserialized data.
|
||||
*
|
||||
* @param data A map of the data associating an instance to the respective identifier from the [ResourceManager].
|
||||
*/
|
||||
fun reload(data: Map<ResourceLocation, T>, registry: HolderLookup.Provider)
|
||||
|
||||
companion object {
|
||||
const val JSON_EXTENSION = ".json"
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -21,7 +21,8 @@ import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
*/
|
||||
data class FriendshipUpdatedEvent(
|
||||
val pokemon: Pokemon,
|
||||
val newFriendshipInitial: Int
|
||||
val newFriendshipInitial: Int,
|
||||
val earned: Boolean = false
|
||||
) {
|
||||
var newFriendship: Int = newFriendshipInitial
|
||||
set(value) {
|
||||
|
||||
@@ -40,7 +40,7 @@ object Fossils: JsonDataRegistry<Fossil> {
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter())
|
||||
.create()
|
||||
|
||||
override val typeToken: TypeToken<Fossil> = TypeToken.get(Fossil::class.java)
|
||||
|
||||
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.api.gui
|
||||
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import net.minecraft.client.gui.components.AbstractWidget
|
||||
import net.minecraft.client.gui.components.Renderable
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener
|
||||
import net.minecraft.client.gui.narration.NarrationElementOutput
|
||||
import net.minecraft.network.chat.Component
|
||||
@@ -55,6 +54,8 @@ abstract class ParentWidget(
|
||||
}
|
||||
|
||||
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||
if (!active) return false
|
||||
|
||||
return children.any {
|
||||
it.mouseClicked(pMouseX, pMouseY, pButton)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.gui
|
||||
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
interface TreeNode<T> {
|
||||
val children: List<TreeNode<T>>
|
||||
}
|
||||
|
||||
class TreeDrawingNode<T>(
|
||||
val value: T,
|
||||
val children: List<TreeDrawingNode<T>>,
|
||||
) {
|
||||
var x = 0
|
||||
var y = 0
|
||||
var offset = 0
|
||||
}
|
||||
|
||||
fun <T> createDrawingTree(treeNode: TreeNode<T>): TreeDrawingNode<T> {
|
||||
val root = setup(treeNode, 0, mutableListOf<Int>(), mutableListOf<Int>())
|
||||
addMods(root, 0)
|
||||
|
||||
return root
|
||||
}
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
private fun <T> setup(
|
||||
treeNode: TreeNode<T>,
|
||||
depth: Int,
|
||||
nexts: MutableList<Int>,
|
||||
offset: MutableList<Int>,
|
||||
): TreeDrawingNode<T> {
|
||||
val children = treeNode.children.map { child ->
|
||||
setup(child, depth + 1, nexts, offset)
|
||||
}
|
||||
|
||||
val treeDrawingNode = TreeDrawingNode<T>(treeNode as T, children)
|
||||
treeDrawingNode.y = depth
|
||||
|
||||
while (nexts.size <= depth) {
|
||||
nexts.add(0)
|
||||
}
|
||||
|
||||
while (offset.size <= depth) {
|
||||
offset.add(0)
|
||||
}
|
||||
|
||||
var place =
|
||||
if (children.isEmpty()) {
|
||||
nexts[depth]
|
||||
} else if (children.size == 1) {
|
||||
// children.first().x - 1
|
||||
children.first().x
|
||||
} else {
|
||||
// (children[0].x + children[1].x) / 2
|
||||
(children.first().x + children.last().x) / 2
|
||||
}
|
||||
|
||||
offset[depth] = max(offset[depth], nexts[depth] - place)
|
||||
|
||||
if (children.isEmpty()) {
|
||||
treeDrawingNode.x = place
|
||||
} else {
|
||||
treeDrawingNode.x = place + offset[depth]
|
||||
}
|
||||
|
||||
nexts[depth] += 2
|
||||
treeDrawingNode.offset = offset[depth]
|
||||
|
||||
return treeDrawingNode
|
||||
}
|
||||
|
||||
private fun <T> addMods(
|
||||
treeNode: TreeDrawingNode<T>,
|
||||
modSum: Int,
|
||||
) {
|
||||
treeNode.x += modSum
|
||||
val newModSum = modSum + treeNode.offset
|
||||
|
||||
treeNode.children.forEach { child ->
|
||||
addMods(child, newModSum)
|
||||
}
|
||||
}
|
||||
|
||||
data class TreeDrawingBounds(val minX: Int, val maxX: Int, val minY: Int, val maxY: Int) {
|
||||
val width = maxX - minX
|
||||
val height = maxY - minY
|
||||
val xCenter = (maxX + minX) / 2.0
|
||||
val yCenter = (maxY + minY) / 2.0
|
||||
}
|
||||
|
||||
fun <T> calculateBounds(treeDrawingNode: TreeDrawingNode<T>): TreeDrawingBounds {
|
||||
var minX = treeDrawingNode.x
|
||||
var maxX = treeDrawingNode.x
|
||||
var minY = treeDrawingNode.y
|
||||
var maxY = treeDrawingNode.y
|
||||
|
||||
for (child in treeDrawingNode.children) {
|
||||
val childBounds = calculateBounds(child)
|
||||
|
||||
minX = min(minX, childBounds.minX)
|
||||
maxX = max(maxX, childBounds.maxX)
|
||||
minY = min(minY, childBounds.minY)
|
||||
maxY = max(maxY, childBounds.maxY)
|
||||
}
|
||||
|
||||
return TreeDrawingBounds(minX, maxX, minY, maxY)
|
||||
}
|
||||
+24
-20
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.api.interaction
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
|
||||
@@ -26,6 +26,7 @@ import com.google.gson.Gson
|
||||
import com.google.gson.GsonBuilder
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import net.minecraft.advancements.critereon.ItemPredicate
|
||||
import net.minecraft.core.HolderLookup
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.server.packs.PackType
|
||||
@@ -36,28 +37,31 @@ import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
import net.minecraft.world.level.material.Fluid
|
||||
|
||||
object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
|
||||
object PokemonInteractions : ServerJsonDataRegistry<PokemonInteractionSet> {
|
||||
override val id = cobblemonResource("pokemon_interactions")
|
||||
override val type = PackType.SERVER_DATA
|
||||
override fun setupParser(registry: HolderLookup.Provider): Gson {
|
||||
return GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter(registry))
|
||||
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
}
|
||||
|
||||
override val observable = SimpleObservable<PokemonInteractions>()
|
||||
override val typeToken: TypeToken<PokemonInteractionSet> = TypeToken.get(PokemonInteractionSet::class.java)
|
||||
override val resourcePath = "pokemon_interactions"
|
||||
override val gson: Gson = GsonBuilder()
|
||||
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
|
||||
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
|
||||
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
|
||||
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
|
||||
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
|
||||
.setPrettyPrinting()
|
||||
.create()
|
||||
|
||||
val interactions = mutableListOf<PokemonInteractionSet>()
|
||||
|
||||
@@ -65,7 +69,7 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
|
||||
PokemonInteractionsSyncPacket(interactions).sendToPlayer(player)
|
||||
}
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>) {
|
||||
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>, registry: HolderLookup.Provider) {
|
||||
interactions.clear()
|
||||
val split = data.entries
|
||||
.partition {
|
||||
@@ -87,4 +91,4 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
|
||||
.flatMap { it.interactions }
|
||||
.firstOrNull(interactionCheck)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,12 +83,6 @@ import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.dimension.DimensionType
|
||||
import net.minecraft.world.level.levelgen.Heightmap
|
||||
import net.minecraft.world.level.pathfinder.PathType
|
||||
import net.minecraft.world.level.storage.loot.LootParams
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.function.Function
|
||||
|
||||
@@ -99,25 +93,31 @@ import java.util.function.Function
|
||||
* @since October 2nd, 2023
|
||||
*/
|
||||
object MoLangFunctions {
|
||||
fun ItemStack.asMoLangValue(registryAccess: RegistryAccess) = ObjectValue(this)
|
||||
.addStandardFunctions()
|
||||
.addFunctions(ItemStackMoLangFunctions.attach(Pair(this, registryAccess)))
|
||||
fun ItemStack.asMoLangValue(registryAccess: RegistryAccess) =
|
||||
ObjectValue(this)
|
||||
.addStandardFunctions()
|
||||
.addFunctions(ItemStackMoLangFunctions.attach(Pair(this, registryAccess)))
|
||||
|
||||
fun Holder<Biome>.asBiomeMoLangValue() = asMoLangValue(Registries.BIOME)
|
||||
.addFunctions(BiomeMoLangFunctions.attach(this))
|
||||
fun Holder<Biome>.asBiomeMoLangValue() =
|
||||
asMoLangValue(Registries.BIOME)
|
||||
.addFunctions(BiomeMoLangFunctions.attach(this))
|
||||
|
||||
fun Holder<Level>.asWorldMoLangValue() = asMoLangValue(Registries.DIMENSION)
|
||||
.addFunctions(WorldMoLangFunctions.attach(this))
|
||||
fun Holder<Level>.asWorldMoLangValue() =
|
||||
asMoLangValue(Registries.DIMENSION)
|
||||
.addFunctions(WorldMoLangFunctions.attach(this))
|
||||
|
||||
fun Holder<Block>.asBlockMoLangValue() = asMoLangValue(Registries.BLOCK)
|
||||
.addFunctions(BlockMoLangFunctions.attach(this))
|
||||
fun Holder<Block>.asBlockMoLangValue() =
|
||||
asMoLangValue(Registries.BLOCK)
|
||||
.addFunctions(BlockMoLangFunctions.attach(this))
|
||||
|
||||
fun MinecraftServer.asMoLangValue() = ObjectValue(this)
|
||||
.addStandardFunctions()
|
||||
.addFunctions(ServerMoLangFunctions.attach(this))
|
||||
fun MinecraftServer.asMoLangValue() =
|
||||
ObjectValue(this)
|
||||
.addStandardFunctions()
|
||||
.addFunctions(ServerMoLangFunctions.attach(this))
|
||||
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() = asMoLangValue(Registries.DIMENSION_TYPE)
|
||||
.addFunctions(DimensionTypeMoLangFunctions.attach(this))
|
||||
fun Holder<DimensionType>.asDimensionTypeMoLangValue() =
|
||||
asMoLangValue(Registries.DIMENSION_TYPE)
|
||||
.addFunctions(DimensionTypeMoLangFunctions.attach(this))
|
||||
|
||||
fun Player.asMoLangValue(): ObjectValue<Player> {
|
||||
if (this is ServerPlayer && uuid in Cobblemon.serverPlayerStructs) {
|
||||
@@ -129,10 +129,11 @@ object MoLangFunctions {
|
||||
}
|
||||
}
|
||||
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.effectiveName().string }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.effectiveName().string },
|
||||
)
|
||||
|
||||
value.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
value.addFunctions(LivingEntityMoLangFunctions.attach(this))
|
||||
@@ -153,57 +154,63 @@ object MoLangFunctions {
|
||||
}
|
||||
|
||||
fun PartyStore.asMoLangValue(): ObjectValue<PartyStore> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() },
|
||||
)
|
||||
value.addFunctions(PokemonStoreMoLangFunctions.attach(this))
|
||||
value.addFunctions(PartyMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun DropEntry.asMoLangValue(): ObjectValue<DropEntry> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() },
|
||||
)
|
||||
value.addFunctions(DropEntryMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun PokemonProperties.asMoLangValue(): ObjectValue<PokemonProperties> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.asString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.asString() },
|
||||
)
|
||||
value.addFunctions(PokemonPropertiesMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun Evolution.asMoLangValue(): ObjectValue<Evolution> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() },
|
||||
)
|
||||
value.addFunctions(EvolutionMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun PCStore.asMoLangValue(): ObjectValue<PCStore> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() },
|
||||
)
|
||||
value.addFunctions(PokemonStoreMoLangFunctions.attach(this))
|
||||
value.addFunctions(PCMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun NPCEntity.asMoLangValue(): ObjectValue<NPCEntity> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.name.string }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.name.string },
|
||||
)
|
||||
value.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
value.addFunctions(LivingEntityMoLangFunctions.attach(this))
|
||||
value.addFunctions(NPCMoLangFunctions.attach(this))
|
||||
@@ -211,10 +218,11 @@ object MoLangFunctions {
|
||||
}
|
||||
|
||||
fun PokemonEntity.asMoLangValue(): ObjectValue<PokemonEntity> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.pokemon.uuid.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.pokemon.uuid.toString() },
|
||||
)
|
||||
|
||||
value.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
value.addFunctions(LivingEntityMoLangFunctions.attach(this))
|
||||
@@ -225,36 +233,40 @@ object MoLangFunctions {
|
||||
}
|
||||
|
||||
fun PokemonBattle.asMoLangValue(): ObjectValue<PokemonBattle> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.battleId.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.battleId.toString() },
|
||||
)
|
||||
value.addFunctions(BattleMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun BattleActor.asMoLangValue(): ObjectValue<BattleActor> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() },
|
||||
)
|
||||
value.addFunctions(BattleActorMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun SpawnablePosition.asMoLangValue(): ObjectValue<SpawnablePosition> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() },
|
||||
)
|
||||
value.addFunctions(SpawnablePositionMoLangFunctions.attach(this))
|
||||
return value
|
||||
}
|
||||
|
||||
fun Vec3.asMoLangValue(): ObjectValue<Vec3> {
|
||||
val value = ObjectValue(
|
||||
obj = this
|
||||
)
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
)
|
||||
value.addFunction("x") { this.x }
|
||||
value.addFunction("y") { this.y }
|
||||
value.addFunction("z") { this.z }
|
||||
@@ -265,45 +277,66 @@ object MoLangFunctions {
|
||||
/**
|
||||
* Different functions exist depending on the entity type, this tries to make the struct that's most specific to the type.
|
||||
*/
|
||||
fun Entity.asMostSpecificMoLangValue(): ObjectValue<out Entity> {
|
||||
return when (this) {
|
||||
is Player -> asMoLangValue()
|
||||
is PokemonEntity -> struct
|
||||
is NPCEntity -> struct
|
||||
is ItemEntity -> ObjectValue(this).also { item ->
|
||||
item.addStandardFunctions()
|
||||
item.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
item.addFunctions(ItemStackMoLangFunctions.attach(Pair(this.item, this.registryAccess())))
|
||||
fun Entity.asMostSpecificMoLangValue(): ObjectValue<out Entity> =
|
||||
when (this) {
|
||||
is Player -> {
|
||||
asMoLangValue()
|
||||
}
|
||||
|
||||
else -> ObjectValue(this).also { objectValue ->
|
||||
objectValue.addStandardFunctions()
|
||||
objectValue.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
if (this is LivingEntity) {
|
||||
objectValue.addFunctions(LivingEntityMoLangFunctions.attach(this))
|
||||
is PokemonEntity -> {
|
||||
struct
|
||||
}
|
||||
|
||||
is NPCEntity -> {
|
||||
struct
|
||||
}
|
||||
|
||||
is ItemEntity -> {
|
||||
ObjectValue(this).also { item ->
|
||||
item.addStandardFunctions()
|
||||
item.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
item.addFunctions(ItemStackMoLangFunctions.attach(Pair(this.item, this.registryAccess())))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
ObjectValue(this).also { objectValue ->
|
||||
objectValue.addStandardFunctions()
|
||||
objectValue.addFunctions(EntityMoLangFunctions.attach(this))
|
||||
if (this is LivingEntity) {
|
||||
objectValue.addFunctions(LivingEntityMoLangFunctions.attach(this))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun <T> Holder<T>.asMoLangValue(key: ResourceKey<Registry<T>>): ObjectValue<Holder<T>> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.unwrapKey().get().location().toString() }
|
||||
)
|
||||
value.functions["is_in"] = Function put@{ params: MoParams ->
|
||||
val tag = TagKey.create(key, ResourceLocation.parse(params.getString(0).replace("#", "")))
|
||||
return@put DoubleValue(if (value.obj.`is`(tag)) 1.0 else 0.0)
|
||||
}
|
||||
value.functions["is_of"] = Function put@{ params: MoParams ->
|
||||
val identifier = ResourceLocation.parse(params.getString(0))
|
||||
return@put DoubleValue(if (value.obj.`is`(identifier)) 1.0 else 0.0)
|
||||
}
|
||||
val value =
|
||||
ObjectValue(
|
||||
obj = this,
|
||||
stringify = {
|
||||
it
|
||||
.unwrapKey()
|
||||
.get()
|
||||
.location()
|
||||
.toString()
|
||||
},
|
||||
)
|
||||
value.functions["is_in"] =
|
||||
Function put@{ params: MoParams ->
|
||||
val tag = TagKey.create(key, ResourceLocation.parse(params.getString(0).replace("#", "")))
|
||||
return@put DoubleValue(if (value.obj.`is`(tag)) 1.0 else 0.0)
|
||||
}
|
||||
value.functions["is_of"] =
|
||||
Function put@{ params: MoParams ->
|
||||
val identifier = ResourceLocation.parse(params.getString(0))
|
||||
return@put DoubleValue(if (value.obj.`is`(identifier)) 1.0 else 0.0)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
fun QueryStruct.addStandardFunctions(): QueryStruct {
|
||||
addFunctions(GeneralMoLangFunctions.holder)
|
||||
addFunctions(GeneralMoLangFunctions.functions)
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -370,23 +403,17 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun moLangFunctionMap(
|
||||
vararg functions: Pair<String, (MoParams) -> MoValue>
|
||||
): Map<String, (MoParams) -> MoValue> {
|
||||
return functions.toMap()
|
||||
}
|
||||
fun moLangFunctionMap(vararg functions: Pair<String, (MoParams) -> MoValue>): Map<String, (MoParams) -> MoValue> = functions.toMap()
|
||||
|
||||
fun queryStructOf(
|
||||
vararg functions: Pair<String, (MoParams) -> MoValue>
|
||||
): QueryStruct {
|
||||
return QueryStruct(
|
||||
fun queryStructOf(vararg functions: Pair<String, (MoParams) -> MoValue>): QueryStruct =
|
||||
QueryStruct(
|
||||
hashMapOf<String, Function<MoParams, Any>>(
|
||||
*functions.map { (name, func) ->
|
||||
name to Function<MoParams, Any> { params -> func(params) }
|
||||
}.toTypedArray()
|
||||
)
|
||||
*functions
|
||||
.map { (name, func) ->
|
||||
name to Function<MoParams, Any> { params -> func(params) }
|
||||
}.toTypedArray(),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fun MoLangRuntime.setup(): MoLangRuntime {
|
||||
environment.query.addStandardFunctions()
|
||||
@@ -395,8 +422,14 @@ object MoLangFunctions {
|
||||
|
||||
fun writeMoValueToNBT(value: MoValue): Tag? {
|
||||
return when (value) {
|
||||
is DoubleValue -> DoubleTag.valueOf(value.value)
|
||||
is StringValue -> StringTag.valueOf(value.value)
|
||||
is DoubleValue -> {
|
||||
DoubleTag.valueOf(value.value)
|
||||
}
|
||||
|
||||
is StringValue -> {
|
||||
StringTag.valueOf(value.value)
|
||||
}
|
||||
|
||||
is ArrayStruct -> {
|
||||
val list = value.map.values
|
||||
val nbtList = ListTag()
|
||||
@@ -413,14 +446,22 @@ object MoLangFunctions {
|
||||
nbt
|
||||
}
|
||||
|
||||
else -> null
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun readMoValueFromNBT(nbt: Tag): MoValue {
|
||||
return when (nbt) {
|
||||
is DoubleTag -> DoubleValue(nbt.asDouble)
|
||||
is StringTag -> StringValue(nbt.asString)
|
||||
fun readMoValueFromNBT(nbt: Tag): MoValue =
|
||||
when (nbt) {
|
||||
is DoubleTag -> {
|
||||
DoubleValue(nbt.asDouble)
|
||||
}
|
||||
|
||||
is StringTag -> {
|
||||
StringValue(nbt.asString)
|
||||
}
|
||||
|
||||
is ListTag -> {
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
var index = 0
|
||||
@@ -441,11 +482,12 @@ object MoLangFunctions {
|
||||
variable
|
||||
}
|
||||
|
||||
else -> VariableStruct()
|
||||
else -> {
|
||||
VariableStruct()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: this one is unused. Should we move it to [MoLangFunctions] object?
|
||||
fun Either<ResourceLocation, ExpressionLike>.runScript(runtime: MoLangRuntime) =
|
||||
map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
|
||||
map({ CobblemonScripts.run(it, runtime) }, { it.resolve(runtime) })
|
||||
|
||||
+9
-10
@@ -17,30 +17,29 @@ import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
|
||||
|
||||
object BattleActorMoLangFunctions : AbstractMoLangFunctionHolder<BattleActor>() {
|
||||
override fun BattleActor.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val actor = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
"is_npc" to { DoubleValue(actor.type == ActorType.NPC) },
|
||||
"is_player" to { DoubleValue(actor.type == ActorType.PLAYER) },
|
||||
"is_wild" to { DoubleValue(actor.type == ActorType.WILD) }
|
||||
)
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["is_npc"] = { DoubleValue(actor.type == ActorType.NPC) }
|
||||
functions["is_player"] = { DoubleValue(actor.type == ActorType.PLAYER) }
|
||||
functions["is_wild"] = { DoubleValue(actor.type == ActorType.WILD) }
|
||||
|
||||
when (actor) {
|
||||
is NPCBattleActor -> {
|
||||
map["npc"] = { actor.entity.struct }
|
||||
functions["npc"] = { actor.entity.struct }
|
||||
}
|
||||
|
||||
is PlayerBattleActor -> {
|
||||
map["player"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
functions["player"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
|
||||
is PokemonBattleActor -> {
|
||||
map["pokemon"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
functions["pokemon"] = { actor.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+14
-12
@@ -16,17 +16,19 @@ import com.cobblemon.mod.common.util.getStringOrNull
|
||||
object BattleMessageMoLangFunctions : AbstractMoLangFunctionHolder<BattleMessage>() {
|
||||
override fun BattleMessage.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val battleMessage = this
|
||||
return mutableMapOf(
|
||||
"has_argument_at" to { params ->
|
||||
val index = params.getInt(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
DoubleValue(value == battleMessage.argumentAt(index))
|
||||
},
|
||||
"has_argument" to { params ->
|
||||
val argumentName = params.getString(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
DoubleValue(value == battleMessage.optionalArgument(argumentName))
|
||||
}
|
||||
)
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["has_argument_at"] = { params ->
|
||||
val index = params.getInt(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
DoubleValue(value == battleMessage.argumentAt(index))
|
||||
}
|
||||
functions["has_argument"] = { params ->
|
||||
val argumentName = params.getString(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
DoubleValue(value == battleMessage.optionalArgument(argumentName))
|
||||
}
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+17
-15
@@ -18,20 +18,22 @@ import java.util.*
|
||||
object BattleMoLangFunctions : AbstractMoLangFunctionHolder<PokemonBattle>() {
|
||||
override fun PokemonBattle.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val battle = this
|
||||
return mutableMapOf(
|
||||
"environment" to { battle.runtime.environment },
|
||||
"battle_id" to { StringValue(battle.battleId.toString()) },
|
||||
"battle_type" to { StringValue(battle.format.toString()) },
|
||||
"is_pvn" to { DoubleValue(battle.isPvN) },
|
||||
"is_pvp" to { DoubleValue(battle.isPvP) },
|
||||
"is_pvw" to { DoubleValue(battle.isPvW) },
|
||||
"stop" to { battle.stop() },
|
||||
"actors" to { battle.actors.toList().asArrayValue { it.struct } },
|
||||
"get_actor" to getActor@{ params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val actor = battle.actors.find { it.uuid == uuid } ?: return@getActor DoubleValue.ZERO
|
||||
actor.struct
|
||||
}
|
||||
)
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["environment"] = { battle.runtime.environment }
|
||||
functions["battle_id"] = { StringValue(battle.battleId.toString()) }
|
||||
functions["battle_type"] = { StringValue(battle.format.toString()) }
|
||||
functions["is_pvn"] = { DoubleValue(battle.isPvN) }
|
||||
functions["is_pvp"] = { DoubleValue(battle.isPvP) }
|
||||
functions["is_pvw"] = { DoubleValue(battle.isPvW) }
|
||||
functions["stop"] = { battle.stop() }
|
||||
functions["actors"] = { battle.actors.toList().asArrayValue { it.struct } }
|
||||
functions["get_actor"] = getActor@{ params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val actor = battle.actors.find { it.uuid == uuid } ?: return@getActor DoubleValue.ZERO
|
||||
actor.struct
|
||||
}
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -12,8 +12,11 @@ import com.bedrockk.molang.runtime.MoParams
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
|
||||
object BiomeMoLangFunctions: AbstractMoLangFunctionHolder<Holder<Biome>>() {
|
||||
object BiomeMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Biome>>() {
|
||||
override fun Holder<Biome>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
return mutableMapOf()
|
||||
val biome = this
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+5
-2
@@ -12,8 +12,11 @@ import com.bedrockk.molang.runtime.MoParams
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.world.level.block.Block
|
||||
|
||||
object BlockMoLangFunctions: AbstractMoLangFunctionHolder<Holder<Block>>() {
|
||||
object BlockMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Block>>() {
|
||||
override fun Holder<Block>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
return mutableMapOf()
|
||||
val block = this
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+4
-1
@@ -14,6 +14,9 @@ import net.minecraft.world.level.dimension.DimensionType
|
||||
|
||||
object DimensionTypeMoLangFunctions : AbstractMoLangFunctionHolder<Holder<DimensionType>>() {
|
||||
override fun Holder<DimensionType>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
return mutableMapOf()
|
||||
val dimension = this
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+7
-6
@@ -18,15 +18,16 @@ import com.cobblemon.mod.common.util.getOrNull
|
||||
object DropEntryMoLangFunctions : AbstractMoLangFunctionHolder<DropEntry>() {
|
||||
override fun DropEntry.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val drop = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["percentage"] = { DoubleValue(drop.percentage.toDouble()) }
|
||||
map["quantity"] = { DoubleValue(drop.quantity.toDouble()) }
|
||||
map["max_selectable_times"] = { DoubleValue(drop.maxSelectableTimes.toDouble()) }
|
||||
map["can_drop"] = { params ->
|
||||
functions["percentage"] = { DoubleValue(drop.percentage.toDouble()) }
|
||||
functions["quantity"] = { DoubleValue(drop.quantity.toDouble()) }
|
||||
functions["max_selectable_times"] = { DoubleValue(drop.maxSelectableTimes.toDouble()) }
|
||||
functions["can_drop"] = { params ->
|
||||
val pokemon = params.getOrNull<ObjectValue<Pokemon>>(0)?.obj
|
||||
DoubleValue(if (drop.canDrop(pokemon)) 1.0 else 0.0)
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+265
-223
@@ -56,258 +56,300 @@ import kotlin.math.sqrt
|
||||
|
||||
object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
|
||||
override fun Entity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val thisEntity = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
"uuid" to { StringValue(thisEntity.uuid.toString()) },
|
||||
"name" to { _ -> StringValue(thisEntity.effectiveName().string) },
|
||||
"yaw" to { _ -> DoubleValue(thisEntity.yRot.toDouble()) },
|
||||
"pitch" to { _ -> DoubleValue(thisEntity.xRot.toDouble()) },
|
||||
"x" to { _ -> DoubleValue(thisEntity.x) },
|
||||
"y" to { _ -> DoubleValue(thisEntity.y) },
|
||||
"z" to { _ -> DoubleValue(thisEntity.z) },
|
||||
"velocity_x" to { _ -> DoubleValue(thisEntity.deltaMovement.x) },
|
||||
"velocity_y" to { _ -> DoubleValue(thisEntity.deltaMovement.y) },
|
||||
"velocity_z" to { _ -> DoubleValue(thisEntity.deltaMovement.z) },
|
||||
"width" to { DoubleValue(thisEntity.boundingBox.xsize) },
|
||||
"height" to { DoubleValue(thisEntity.boundingBox.ysize) },
|
||||
"this_size" to { DoubleValue(thisEntity.boundingBox.run { if (xsize > ysize) xsize else ysize }) },
|
||||
"this_width" to { DoubleValue(thisEntity.boundingBox.xsize) },
|
||||
"this_height" to { DoubleValue(thisEntity.boundingBox.ysize) },
|
||||
"id_modulo" to { params -> DoubleValue(thisEntity.uuid.hashCode() % params.getDouble(0)) },
|
||||
"horizontal_velocity" to { _ -> DoubleValue(thisEntity.deltaMovement.horizontalDistance()) },
|
||||
"vertical_velocity" to { DoubleValue(thisEntity.deltaMovement.y) },
|
||||
"is_on_ground" to { _ -> DoubleValue(thisEntity.onGround()) },
|
||||
"world" to { _ -> thisEntity.level().worldRegistry.wrapAsHolder(thisEntity.level()).asWorldMoLangValue() },
|
||||
"biome" to { _ -> thisEntity.level().getBiome(thisEntity.blockPosition()).asBiomeMoLangValue() },
|
||||
"is_passenger" to { DoubleValue(thisEntity.isPassenger) },
|
||||
"discard" to { thisEntity.discard() },
|
||||
"is_sneaking" to { _ -> DoubleValue(thisEntity.isShiftKeyDown) },
|
||||
"is_sprinting" to { _ -> DoubleValue(thisEntity.isSprinting) },
|
||||
"is_in_water" to { _ -> DoubleValue(thisEntity.isUnderWater) },
|
||||
"is_in_rain" to { _ -> DoubleValue(thisEntity.isInWaterOrRain && !thisEntity.isInWater) },
|
||||
"is_touching_water_or_rain" to { _ -> DoubleValue(thisEntity.isInWaterRainOrBubble) },
|
||||
"is_touching_water" to { _ -> DoubleValue(thisEntity.isInWater) },
|
||||
"is_underwater" to { DoubleValue(thisEntity.getIsSubmerged()) },
|
||||
"is_in_lava" to { _ -> DoubleValue(thisEntity.isInLava) },
|
||||
"is_on_fire" to { _ -> DoubleValue(thisEntity.isOnFire) },
|
||||
"is_invisible" to { _ -> DoubleValue(thisEntity.isInvisible) },
|
||||
"is_riding" to { _ -> DoubleValue(thisEntity.isPassenger) },
|
||||
"set_name" to { params ->
|
||||
val name = params.getString(0)
|
||||
thisEntity.customName = name.text()
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"damage" to { params ->
|
||||
val amount = params.getDouble(0)
|
||||
val source = DamageSource(
|
||||
thisEntity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolder(DamageTypes.GENERIC).get()
|
||||
val entity = this
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["uuid"] = { StringValue(entity.uuid.toString()) }
|
||||
functions["name"] = { StringValue(entity.effectiveName().string) }
|
||||
functions["yaw"] = { DoubleValue(entity.yRot.toDouble()) }
|
||||
functions["pitch"] = { DoubleValue(entity.xRot.toDouble()) }
|
||||
functions["x"] = { DoubleValue(entity.x) }
|
||||
functions["y"] = { DoubleValue(entity.y) }
|
||||
functions["z"] = { DoubleValue(entity.z) }
|
||||
functions["velocity_x"] = { DoubleValue(entity.deltaMovement.x) }
|
||||
functions["velocity_y"] = { DoubleValue(entity.deltaMovement.y) }
|
||||
functions["velocity_z"] = { DoubleValue(entity.deltaMovement.z) }
|
||||
functions["width"] = { DoubleValue(entity.boundingBox.xsize) }
|
||||
functions["height"] = { DoubleValue(entity.boundingBox.ysize) }
|
||||
functions["this_size"] = { DoubleValue(entity.boundingBox.run { if (xsize > ysize) xsize else ysize }) }
|
||||
functions["this_width"] = { DoubleValue(entity.boundingBox.xsize) }
|
||||
functions["this_height"] = { DoubleValue(entity.boundingBox.ysize) }
|
||||
functions["id_modulo"] = { params -> DoubleValue(entity.uuid.hashCode() % params.getDouble(0)) }
|
||||
functions["horizontal_velocity"] = { DoubleValue(entity.deltaMovement.horizontalDistance()) }
|
||||
functions["vertical_velocity"] = { DoubleValue(entity.deltaMovement.y) }
|
||||
functions["is_on_ground"] = { DoubleValue(entity.onGround()) }
|
||||
functions["world"] = {
|
||||
entity
|
||||
.level()
|
||||
.worldRegistry
|
||||
.wrapAsHolder(entity.level())
|
||||
.asWorldMoLangValue()
|
||||
}
|
||||
functions["biome"] = { entity.level().getBiome(entity.blockPosition()).asBiomeMoLangValue() }
|
||||
functions["is_passenger"] = { DoubleValue(entity.isPassenger) }
|
||||
functions["discard"] = { entity.discard() }
|
||||
functions["is_sneaking"] = { DoubleValue(entity.isShiftKeyDown) }
|
||||
functions["is_sprinting"] = { DoubleValue(entity.isSprinting) }
|
||||
functions["is_in_water"] = { DoubleValue(entity.isUnderWater) }
|
||||
functions["is_in_rain"] = { DoubleValue(entity.isInWaterOrRain && !entity.isInWater) }
|
||||
functions["is_touching_water_or_rain"] = { DoubleValue(entity.isInWaterRainOrBubble) }
|
||||
functions["is_touching_water"] = { DoubleValue(entity.isInWater) }
|
||||
functions["is_underwater"] = { DoubleValue(entity.getIsSubmerged()) }
|
||||
functions["is_in_lava"] = { DoubleValue(entity.isInLava) }
|
||||
functions["is_on_fire"] = { DoubleValue(entity.isOnFire) }
|
||||
functions["is_invisible"] = { DoubleValue(entity.isInvisible) }
|
||||
functions["is_riding"] = { DoubleValue(entity.isPassenger) }
|
||||
functions["set_name"] = { params ->
|
||||
val name = params.getString(0)
|
||||
entity.customName = name.text()
|
||||
DoubleValue.ONE
|
||||
}
|
||||
functions["damage"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
val source =
|
||||
DamageSource(
|
||||
entity
|
||||
.level()
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
.getHolder(DamageTypes.GENERIC)
|
||||
.get(),
|
||||
)
|
||||
thisEntity.hurt(source, amount.toFloat())
|
||||
},
|
||||
"distance_to_owner" to distanceToOwner@{
|
||||
if (thisEntity !is TamableAnimal) {
|
||||
return@distanceToOwner DoubleValue.ZERO
|
||||
}
|
||||
val owner = thisEntity.owner ?: return@distanceToOwner DoubleValue.ZERO
|
||||
DoubleValue(thisEntity.distanceTo(owner))
|
||||
},
|
||||
"owner" to owner@{
|
||||
if (thisEntity !is TamableAnimal) return@owner DoubleValue.ZERO
|
||||
val owner = thisEntity.owner
|
||||
owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
|
||||
},
|
||||
"delta_movement" to {
|
||||
listOf(thisEntity.deltaMovement.x, thisEntity.deltaMovement.y, thisEntity.deltaMovement.z)
|
||||
.asArrayValue(::DoubleValue)
|
||||
},
|
||||
"tags" to {
|
||||
val tags = thisEntity.tags
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
|
||||
array
|
||||
},
|
||||
"add_tag" to { params ->
|
||||
val tag = params.getString(0)
|
||||
thisEntity.addTag(tag)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"remove_tag" to { params ->
|
||||
val tag = params.getString(0)
|
||||
thisEntity.removeTag(tag)
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"has_tag" to { params ->
|
||||
val tag = params.getString(0)
|
||||
DoubleValue(thisEntity.tags.contains(tag))
|
||||
},
|
||||
"distance_to_pos" to { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
DoubleValue(sqrt(thisEntity.distanceToSqr(Vec3(x, y, z))))
|
||||
},
|
||||
"type" to { _ ->
|
||||
thisEntity.registryAccess().registry(Registries.ENTITY_TYPE).get().getKey(thisEntity.type)?.toString()?.let {
|
||||
entity.hurt(source, amount.toFloat())
|
||||
}
|
||||
functions["distance_to_owner"] = distanceToOwner@{
|
||||
if (entity !is TamableAnimal) {
|
||||
return@distanceToOwner DoubleValue.ZERO
|
||||
}
|
||||
val owner = entity.owner ?: return@distanceToOwner DoubleValue.ZERO
|
||||
DoubleValue(entity.distanceTo(owner))
|
||||
}
|
||||
functions["owner"] = owner@{
|
||||
if (entity !is TamableAnimal) return@owner DoubleValue.ZERO
|
||||
val owner = entity.owner
|
||||
owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
|
||||
}
|
||||
functions["delta_movement"] = {
|
||||
listOf(entity.deltaMovement.x, entity.deltaMovement.y, entity.deltaMovement.z)
|
||||
.asArrayValue(::DoubleValue)
|
||||
}
|
||||
functions["tags"] = {
|
||||
val tags = entity.tags
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
tags.forEachIndexed { index, tag -> array.setDirectly("$index", StringValue(tag)) }
|
||||
array
|
||||
}
|
||||
functions["add_tag"] = { params ->
|
||||
val tag = params.getString(0)
|
||||
entity.addTag(tag)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
functions["remove_tag"] = { params ->
|
||||
val tag = params.getString(0)
|
||||
entity.removeTag(tag)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
functions["has_tag"] = { params ->
|
||||
val tag = params.getString(0)
|
||||
DoubleValue(entity.tags.contains(tag))
|
||||
}
|
||||
functions["distance_to_pos"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
DoubleValue(sqrt(entity.distanceToSqr(Vec3(x, y, z))))
|
||||
}
|
||||
functions["type"] = {
|
||||
entity
|
||||
.registryAccess()
|
||||
.registry(Registries.ENTITY_TYPE)
|
||||
.get()
|
||||
.getKey(entity.type)
|
||||
?.toString()
|
||||
?.let {
|
||||
StringValue(it)
|
||||
} ?: DoubleValue.ZERO
|
||||
},
|
||||
"find_nearby_block" to findNearbyBlock@{ params ->
|
||||
val input = params.getString(0)
|
||||
val isTag = input.contains("#")
|
||||
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
val range = params.getDoubleOrNull(1) ?: 10
|
||||
val blockPos = thisEntity.level().getBlockStatesWithPos(
|
||||
AABB.ofSize(
|
||||
thisEntity.position(),
|
||||
range.toDouble(),
|
||||
range.toDouble(),
|
||||
range.toDouble()
|
||||
)
|
||||
)
|
||||
.filter { blockPosPair ->
|
||||
}
|
||||
functions["find_nearby_block"] = findNearbyBlock@{ params ->
|
||||
val input = params.getString(0)
|
||||
val isTag = input.contains("#")
|
||||
val type = input.replace("#", "").asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
val range = params.getDoubleOrNull(1) ?: 10
|
||||
val blockPos =
|
||||
entity
|
||||
.level()
|
||||
.getBlockStatesWithPos(
|
||||
AABB.ofSize(
|
||||
entity.position(),
|
||||
range.toDouble(),
|
||||
range.toDouble(),
|
||||
range.toDouble(),
|
||||
),
|
||||
).filter { blockPosPair ->
|
||||
blockPosPair.first.blockHolder.let {
|
||||
if (isTag) it.`is`(
|
||||
TagKey.create(
|
||||
Registries.BLOCK,
|
||||
type
|
||||
if (isTag) {
|
||||
it.`is`(
|
||||
TagKey.create(
|
||||
Registries.BLOCK,
|
||||
type,
|
||||
),
|
||||
)
|
||||
) else it.`is`(type)
|
||||
} else {
|
||||
it.`is`(type)
|
||||
}
|
||||
}
|
||||
}
|
||||
.minByOrNull { it.second.distSqr(thisEntity.blockPosition()) }
|
||||
}.minByOrNull { it.second.distSqr(entity.blockPosition()) }
|
||||
?.second
|
||||
if (blockPos != null) {
|
||||
return@findNearbyBlock ArrayStruct(
|
||||
mapOf(
|
||||
"0" to DoubleValue(blockPos.x),
|
||||
"1" to DoubleValue(blockPos.y),
|
||||
"2" to DoubleValue(blockPos.z)
|
||||
)
|
||||
)
|
||||
}
|
||||
return@findNearbyBlock DoubleValue.ZERO
|
||||
},
|
||||
"get_nearby_entities" to { params ->
|
||||
val distance = params.getDouble(0)
|
||||
val entities =
|
||||
thisEntity.level().getEntities(thisEntity, AABB.ofSize(thisEntity.position(), distance, distance, distance))
|
||||
if (blockPos != null) {
|
||||
return@findNearbyBlock ArrayStruct(
|
||||
mapOf(
|
||||
"0" to DoubleValue(blockPos.x),
|
||||
"1" to DoubleValue(blockPos.y),
|
||||
"2" to DoubleValue(blockPos.z),
|
||||
),
|
||||
)
|
||||
}
|
||||
return@findNearbyBlock DoubleValue.ZERO
|
||||
}
|
||||
functions["get_nearby_entities"] = { params ->
|
||||
val distance = params.getDouble(0)
|
||||
val entities =
|
||||
entity
|
||||
.level()
|
||||
.getEntities(entity, AABB.ofSize(entity.position(), distance, distance, distance))
|
||||
|
||||
entities
|
||||
.filterIsInstance<Entity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
},
|
||||
"is_standing_on_blocks" to { params ->
|
||||
val depth = params.getDouble(0).toInt()
|
||||
val blockStrings: MutableSet<String> = mutableSetOf()
|
||||
for (blockIndex in 1..<params.params.size) {
|
||||
blockStrings.add(params.getString(blockIndex))
|
||||
}
|
||||
entities
|
||||
.filterIsInstance<Entity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
functions["is_standing_on_blocks"] = { params ->
|
||||
val depth = params.getDouble(0).toInt()
|
||||
val blockStrings: MutableSet<String> = mutableSetOf()
|
||||
for (blockIndex in 1..<params.params.size) {
|
||||
blockStrings.add(params.getString(blockIndex))
|
||||
}
|
||||
|
||||
if (thisEntity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
|
||||
},
|
||||
if (entity.isStandingOn(blockStrings, depth)) DoubleValue.ONE else DoubleValue.ZERO
|
||||
}
|
||||
|
||||
// q.entity.spawn_bedrock_particles(effect, locator, [player])
|
||||
// sends to everyone nearby or just to the player if they're set.
|
||||
// Locator is necessary even if unused on non-posables.
|
||||
"spawn_bedrock_particles" to { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
val locator = params.getString(1)
|
||||
val player = params.getOrNull<MoValue>(2)?.let {
|
||||
// q.entity.spawn_bedrock_particles(effect, locator, [player])
|
||||
// sends to everyone nearby or just to the player if they're set.
|
||||
// Locator is necessary even if unused on non-posables.
|
||||
functions["spawn_bedrock_particles"] = { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
val locator = params.getString(1)
|
||||
val player =
|
||||
params.getOrNull<MoValue>(2)?.let {
|
||||
when (it) {
|
||||
is StringValue -> thisEntity.level().getPlayerByUUID(UUID.fromString(it.value))
|
||||
is StringValue -> entity.level().getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
|
||||
val packet = SpawnSnowstormEntityParticlePacket(particle, thisEntity.id, listOf(locator))
|
||||
if (player == null) {
|
||||
packet.sendToPlayersAround(thisEntity.x, thisEntity.y, thisEntity.z, 64.0, thisEntity.level().dimension())
|
||||
} else {
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
},
|
||||
"make_intangible" to { params ->
|
||||
val intangible = params.getBooleanOrNull(0) ?: true
|
||||
thisEntity.noPhysics = intangible
|
||||
DoubleValue.ONE
|
||||
val packet = SpawnSnowstormEntityParticlePacket(particle, entity.id, listOf(locator))
|
||||
if (player == null) {
|
||||
packet.sendToPlayersAround(
|
||||
entity.x,
|
||||
entity.y,
|
||||
entity.z,
|
||||
64.0,
|
||||
entity.level().dimension(),
|
||||
)
|
||||
} else {
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
)
|
||||
}
|
||||
functions["make_intangible"] = { params ->
|
||||
val intangible = params.getBooleanOrNull(0) ?: true
|
||||
entity.noPhysics = intangible
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
|
||||
if (thisEntity is PosableEntity) {
|
||||
map["play_animation"] = playAnimation@{ params ->
|
||||
val animation = params.getString(0)
|
||||
val packet = PlayPosableAnimationPacket(thisEntity.id, setOf(animation), emptyList())
|
||||
val target = params.getStringOrNull(1)
|
||||
if (target != null) {
|
||||
val targetPlayer = if (target.asUUID != null) {
|
||||
thisEntity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
|
||||
} else if (thisEntity.level() is ServerLevel) {
|
||||
thisEntity.level().server!!.playerList.getPlayerByName(target)
|
||||
when (entity) {
|
||||
is PosableEntity -> {
|
||||
functions["play_animation"] = playAnimation@{ params ->
|
||||
val animation = params.getString(0)
|
||||
val packet = PlayPosableAnimationPacket(entity.id, setOf(animation), emptyList())
|
||||
val target = params.getStringOrNull(1)
|
||||
if (target != null) {
|
||||
val targetPlayer =
|
||||
if (target.asUUID != null) {
|
||||
entity.level().getPlayerByUUID(target.asUUID!!) as ServerPlayer
|
||||
} else if (entity.level() is ServerLevel) {
|
||||
entity
|
||||
.level()
|
||||
.server!!
|
||||
.playerList
|
||||
.getPlayerByName(target)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (targetPlayer != null) {
|
||||
packet.sendToPlayer(targetPlayer)
|
||||
return@playAnimation DoubleValue.ONE
|
||||
} else {
|
||||
return@playAnimation DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (targetPlayer != null) {
|
||||
packet.sendToPlayer(targetPlayer)
|
||||
packet.sendToPlayersAround(
|
||||
entity.x,
|
||||
entity.y,
|
||||
entity.z,
|
||||
64.0,
|
||||
entity.level().dimension(),
|
||||
)
|
||||
return@playAnimation DoubleValue.ONE
|
||||
} else {
|
||||
return@playAnimation DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
packet.sendToPlayersAround(thisEntity.x, thisEntity.y, thisEntity.z, 64.0, thisEntity.level().dimension())
|
||||
return@playAnimation DoubleValue.ONE
|
||||
}
|
||||
|
||||
functions["face"] = { params ->
|
||||
ObjectValue(ReferenceDialogueFaceProvider(entity.id, params.getBooleanOrNull(0) != false))
|
||||
}
|
||||
}
|
||||
|
||||
map["face"] = { params ->
|
||||
ObjectValue(ReferenceDialogueFaceProvider(thisEntity.id, params.getBooleanOrNull(0) != false))
|
||||
|
||||
is Schedulable -> {
|
||||
functions["run_molang_after"] = { params ->
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDouble(1).toFloat()
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
entity.after(delayInSeconds) {
|
||||
runtime.resolve(expression)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisEntity is Schedulable) {
|
||||
map["run_molang_after"] = { params ->
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDouble(1).toFloat()
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
thisEntity.after(delayInSeconds) {
|
||||
runtime.resolve(expression)
|
||||
is MoLangScriptingEntity -> {
|
||||
functions["add_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
val allowDuplicates = params.getBooleanOrNull(2) ?: false
|
||||
entity.callbacks.addCallback(type, callback, allowDuplicates)
|
||||
}
|
||||
functions["remove_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
entity.callbacks.removeCallback(type, callback)
|
||||
}
|
||||
functions["has_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
DoubleValue(callback in (entity.callbacks[type] ?: emptySet()))
|
||||
}
|
||||
functions["clear_callbacks"] = { params ->
|
||||
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
if (type != null) {
|
||||
entity.callbacks[type]?.clear()
|
||||
} else {
|
||||
entity.callbacks.clear()
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisEntity is MoLangScriptingEntity) {
|
||||
map["add_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
val allowDuplicates = params.getBooleanOrNull(2) ?: false
|
||||
thisEntity.callbacks.addCallback(type, callback, allowDuplicates)
|
||||
}
|
||||
map["remove_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
thisEntity.callbacks.removeCallback(type, callback)
|
||||
}
|
||||
map["has_callback"] = { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
DoubleValue(callback in (thisEntity.callbacks[type] ?: emptySet()))
|
||||
}
|
||||
map["clear_callbacks"] = { params ->
|
||||
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
if (type != null) {
|
||||
thisEntity.callbacks[type]?.clear()
|
||||
} else {
|
||||
thisEntity.callbacks.clear()
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+8
-8
@@ -19,26 +19,26 @@ import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
object EvolutionMoLangFunctions : AbstractMoLangFunctionHolder<Evolution>() {
|
||||
override fun Evolution.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val evolution = this
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["result"] = { evolution.result.asMoLangValue() }
|
||||
map["is_optional"] = { DoubleValue(evolution.optional) }
|
||||
map["consumes_held_item"] = { DoubleValue(evolution.consumeHeldItem) }
|
||||
functions["result"] = { evolution.result.asMoLangValue() }
|
||||
functions["is_optional"] = { DoubleValue(evolution.optional) }
|
||||
functions["consumes_held_item"] = { DoubleValue(evolution.consumeHeldItem) }
|
||||
|
||||
when (evolution) {
|
||||
is LevelUpEvolution -> {
|
||||
map["is_level_up"] = { DoubleValue.ONE }
|
||||
functions["is_level_up"] = { DoubleValue.ONE }
|
||||
}
|
||||
|
||||
is TradeEvolution -> {
|
||||
map["is_trade"] = { DoubleValue.ONE }
|
||||
functions["is_trade"] = { DoubleValue.ONE }
|
||||
}
|
||||
|
||||
is ItemInteractionEvolution -> {
|
||||
map["is_item"] = { DoubleValue.ONE }
|
||||
functions["is_item"] = { DoubleValue.ONE }
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+107
-101
@@ -42,101 +42,104 @@ import net.minecraft.world.item.ItemStack
|
||||
import java.text.ParseException
|
||||
|
||||
object GeneralMoLangFunctions {
|
||||
val holder: HashMap<String, (MoParams) -> Any> = hashMapOf(
|
||||
"print" to { params ->
|
||||
val functions: MutableMap<String, (MoParams) -> Any> = mutableMapOf()
|
||||
|
||||
init {
|
||||
functions["print"] = { params ->
|
||||
val message = params.get<MoValue>(0).asString()
|
||||
LOGGER.info(message)
|
||||
},
|
||||
"delete_variable" to deleteVar@{ params ->
|
||||
}
|
||||
functions["delete_variable"] = { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
struct.map.remove(variable)
|
||||
return@deleteVar DoubleValue.ONE
|
||||
},
|
||||
"delete_variables" to { params ->
|
||||
DoubleValue.ONE
|
||||
}
|
||||
functions["delete_variables"] = { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
struct.map.clear()
|
||||
DoubleValue.ONE
|
||||
},
|
||||
"get_variable" to { params ->
|
||||
}
|
||||
functions["get_variable"] = { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
struct.map[variable] ?: DoubleValue.ZERO
|
||||
},
|
||||
"set_variable" to { params ->
|
||||
}
|
||||
functions["set_variable"] = { params ->
|
||||
val struct = params.get<VariableStruct>(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
struct.map[variable] = value
|
||||
value
|
||||
},
|
||||
"set_query" to { params ->
|
||||
}
|
||||
functions["set_query"] = { params ->
|
||||
val variable = params.getString(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
params.environment.query.addFunction(variable) { value }
|
||||
value
|
||||
},
|
||||
"replace" to { params ->
|
||||
}
|
||||
functions["replace"] = { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
val replace = params.getString(2)
|
||||
StringValue(text.replace(search, replace))
|
||||
},
|
||||
"is_included" to { params ->
|
||||
}
|
||||
functions["is_included"] = { params ->
|
||||
val text = params.getString(0)
|
||||
val search = params.getString(1)
|
||||
DoubleValue(text.contains(search))
|
||||
},
|
||||
"to_lower" to { params ->
|
||||
}
|
||||
functions["to_lower"] = { params ->
|
||||
StringValue(params.getString(0).lowercase())
|
||||
},
|
||||
"to_upper" to { params ->
|
||||
}
|
||||
functions["to_upper"] = { params ->
|
||||
StringValue(params.getString(0).uppercase())
|
||||
},
|
||||
"string_length" to { params ->
|
||||
}
|
||||
functions["string_length"] = { params ->
|
||||
DoubleValue(params.getString(0).length)
|
||||
},
|
||||
"split_string" to { params ->
|
||||
}
|
||||
functions["split_string"] = { params ->
|
||||
val text = params.getString(0)
|
||||
val delimiter = params.getString(1)
|
||||
val parts = text.split(delimiter).map { StringValue(it) }
|
||||
val struct = ArrayStruct(hashMapOf())
|
||||
parts.forEachIndexed { index, moValue -> struct.setDirectly("$index", moValue) }
|
||||
struct
|
||||
},
|
||||
"is_blank" to { params ->
|
||||
}
|
||||
functions["is_blank"] = { params ->
|
||||
val arg = params.get<MoValue>(0)
|
||||
DoubleValue((arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) || (arg is DoubleValue && arg.value == 0.0))
|
||||
},
|
||||
"run_command" to { params ->
|
||||
DoubleValue(
|
||||
(arg is StringValue && (arg.value.isBlank() || arg.value.toDoubleOrNull() == 0.0)) ||
|
||||
(arg is DoubleValue && arg.value == 0.0),
|
||||
)
|
||||
}
|
||||
functions["run_command"] = { params ->
|
||||
val command = params.getString(0)
|
||||
try {
|
||||
server()!!.commands.performPrefixedCommand(server()!!.createCommandSourceStack(), command)
|
||||
} catch (_: NullPointerException) {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
},
|
||||
"is_int" to { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) },
|
||||
"is_number" to { params ->
|
||||
}
|
||||
functions["is_int"] = { params -> DoubleValue(params.get<MoValue>(0).asString().isInt()) }
|
||||
functions["is_number"] = { params ->
|
||||
DoubleValue(
|
||||
params.get<MoValue>(0).asString().toDoubleOrNull() != null
|
||||
params.get<MoValue>(0).asString().toDoubleOrNull() != null,
|
||||
)
|
||||
},
|
||||
"to_number" to { params ->
|
||||
}
|
||||
functions["to_number"] = { params ->
|
||||
DoubleValue(
|
||||
params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0
|
||||
params.get<MoValue>(0).asString().toDoubleOrNull() ?: 0.0,
|
||||
)
|
||||
},
|
||||
"to_int" to { params ->
|
||||
}
|
||||
functions["to_int"] = { params ->
|
||||
DoubleValue(
|
||||
params.get<MoValue>(0).asString().toIntOrNull() ?: 0
|
||||
params.get<MoValue>(0).asString().toIntOrNull() ?: 0,
|
||||
)
|
||||
},
|
||||
"to_string" to { params -> StringValue(params.get<MoValue>(0).asString()) },
|
||||
"do_effect_walks" to { _ ->
|
||||
DoubleValue(Cobblemon.config.walkingInBattleAnimations)
|
||||
},
|
||||
"random" to { params ->
|
||||
}
|
||||
functions["to_string"] = { params -> StringValue(params.get<MoValue>(0).asString()) }
|
||||
functions["do_effect_walks"] = { DoubleValue(Cobblemon.config.walkingInBattleAnimations) }
|
||||
functions["random"] = { params ->
|
||||
val options = mutableListOf<MoValue>()
|
||||
var index = 0
|
||||
while (params.contains(index)) {
|
||||
@@ -144,35 +147,35 @@ object GeneralMoLangFunctions {
|
||||
index++
|
||||
}
|
||||
options.random() // Can throw an exception if they specified no args. They'd be idiots though.
|
||||
},
|
||||
"curve" to { params ->
|
||||
}
|
||||
functions["curve"] = { params ->
|
||||
val curveName = params.getString(0)
|
||||
val curve =
|
||||
WaveFunctions.functions[curveName] ?: throw IllegalArgumentException("Unknown curve: $curveName")
|
||||
ObjectValue(curve)
|
||||
},
|
||||
"array" to { params ->
|
||||
}
|
||||
functions["array"] = { params ->
|
||||
val values = params.params
|
||||
val array = ArrayStruct(hashMapOf())
|
||||
values.forEachIndexed { index, moValue -> array.setDirectly("$index", moValue) }
|
||||
array
|
||||
},
|
||||
"length" to { params ->
|
||||
}
|
||||
functions["length"] = { params ->
|
||||
val array = (params.getOrNull<MoValue>(0) as? VariableStruct)
|
||||
if (array == null) {
|
||||
DoubleValue.ZERO
|
||||
} else {
|
||||
DoubleValue(array.map.size.toDouble())
|
||||
}
|
||||
},
|
||||
"append" to { params ->
|
||||
}
|
||||
functions["append"] = { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val value = params.get<MoValue>(1)
|
||||
val nextIndex = array.map.size
|
||||
array.setDirectly("$nextIndex", value)
|
||||
array
|
||||
},
|
||||
"insert" to { params ->
|
||||
}
|
||||
functions["insert"] = { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val index = params.getInt(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
@@ -187,36 +190,40 @@ object GeneralMoLangFunctions {
|
||||
}
|
||||
array.map[index.toString()] = value
|
||||
array
|
||||
},
|
||||
"delete" to { params ->
|
||||
}
|
||||
functions["delete"] = { params ->
|
||||
val array = params.get<ArrayStruct>(0)
|
||||
val index = params.getInt(1)
|
||||
if (index in 0 until array.map.size) {
|
||||
array.map.remove(index.toString())
|
||||
// Re-index the array to keep keys sequential in numerical order
|
||||
val newMap = hashMapOf<String, MoValue>()
|
||||
array.map.keys.mapNotNull { it.toIntOrNull() }
|
||||
array.map.keys
|
||||
.mapNotNull { it.toIntOrNull() }
|
||||
.sorted()
|
||||
.forEachIndexed { i, k -> newMap[i.toString()] = array.map[k.toString()]!! }
|
||||
array.map.clear()
|
||||
array.map.putAll(newMap)
|
||||
}
|
||||
array
|
||||
},
|
||||
"run_script" to { params ->
|
||||
}
|
||||
functions["run_script"] = { params ->
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.query = params.environment.query
|
||||
runtime.environment.variable = params.environment.variable
|
||||
val args = params.params.subList(1, params.params.size)
|
||||
runtime.environment.context = ContextStruct(
|
||||
(params.environment.context?.map
|
||||
?: emptyMap()) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap()
|
||||
)
|
||||
runtime.environment.context =
|
||||
ContextStruct(
|
||||
(
|
||||
params.environment.context?.map
|
||||
?: emptyMap()
|
||||
) + args.mapIndexed { index, value -> "arg_${index + 1}" to value }.toMap(),
|
||||
)
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
// store the args in the
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue.ZERO
|
||||
},
|
||||
"run_molang" to { params ->
|
||||
}
|
||||
functions["run_molang"] = { params ->
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.query = params.environment.query
|
||||
runtime.environment.variable = params.environment.variable
|
||||
@@ -224,17 +231,16 @@ object GeneralMoLangFunctions {
|
||||
val expression = params.getString(0).asExpressionLike()
|
||||
val delayInSeconds = params.getDoubleOrNull(1)?.toFloat() ?: 0.0f
|
||||
|
||||
fun evaluate(): MoValue {
|
||||
return try {
|
||||
fun evaluate(): MoValue =
|
||||
try {
|
||||
runtime.resolve(expression)
|
||||
} catch (ex: Exception) {
|
||||
LOGGER.warn(
|
||||
"Failed to evaluate MoLang expression${if (delayInSeconds > 0.0f) " (delayed)" else ""}: $expression",
|
||||
ex
|
||||
ex,
|
||||
)
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
if (delayInSeconds > 0.0f) {
|
||||
val tracker =
|
||||
@@ -246,55 +252,55 @@ object GeneralMoLangFunctions {
|
||||
} else {
|
||||
evaluate()
|
||||
}
|
||||
},
|
||||
"system_time_millis" to { _ ->
|
||||
DoubleValue(System.currentTimeMillis())
|
||||
},
|
||||
}
|
||||
functions["system_time_millis"] = { DoubleValue(System.currentTimeMillis()) }
|
||||
// the rest of the world use dd/MM/yyyy grow up america (this comment was generated by copilot)
|
||||
"date_local_time" to { _ ->
|
||||
functions["date_local_time"] = {
|
||||
val time = System.currentTimeMillis()
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_of" to { params ->
|
||||
}
|
||||
functions["date_of"] = { params ->
|
||||
val time = params.getDouble(0).toLong()
|
||||
val date = java.util.Date(time)
|
||||
val formatted = java.text.SimpleDateFormat("dd/MM/yyyy").format(date)
|
||||
StringValue(formatted)
|
||||
},
|
||||
"date_is_after" to { params ->
|
||||
}
|
||||
functions["date_is_after"] = { params ->
|
||||
val dateA = params.getString(0)
|
||||
val dateB = params.getString(1)
|
||||
val format = java.text.SimpleDateFormat("dd/MM/yyyy")
|
||||
val a = try {
|
||||
format.parse(dateA)
|
||||
} catch (_: ParseException) {
|
||||
throw RuntimeException("Cannot parse date ${dateA}, expected it to be in dd/MM/yyyy format, other provided")
|
||||
}
|
||||
val b = try {
|
||||
format.parse(dateB)
|
||||
} catch (_: ParseException) {
|
||||
throw RuntimeException("Cannot parse date ${dateB}, expected it to be in dd/MM/yyyy format, other provided")
|
||||
}
|
||||
val a =
|
||||
try {
|
||||
format.parse(dateA)
|
||||
} catch (_: ParseException) {
|
||||
throw RuntimeException("Cannot parse date $dateA, expected it to be in dd/MM/yyyy format, other provided")
|
||||
}
|
||||
val b =
|
||||
try {
|
||||
format.parse(dateB)
|
||||
} catch (_: ParseException) {
|
||||
throw RuntimeException("Cannot parse date $dateB, expected it to be in dd/MM/yyyy format, other provided")
|
||||
}
|
||||
DoubleValue(a.after(b))
|
||||
},
|
||||
"get_move_from_id" to { params ->
|
||||
}
|
||||
functions["get_move_from_id"] = { params ->
|
||||
val moveId = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveId)
|
||||
moveTemplate?.struct ?: DoubleValue.ZERO
|
||||
},
|
||||
"create_simple_party_provider" to { _ ->
|
||||
}
|
||||
functions["create_simple_party_provider"] = {
|
||||
val partyProvider = SimplePartyProvider()
|
||||
partyProvider.struct
|
||||
},
|
||||
"create_pickup_item" to { params ->
|
||||
}
|
||||
functions["create_pickup_item"] = { params ->
|
||||
val item = params.getOrNull<MoValue>(0)?.asString()?.let(ObtainableItemCondition::parseFromString)
|
||||
val pickupPriority = params.getIntOrNull(1) ?: 0
|
||||
val pickupItem = ObtainableItem(item = item, pickupPriority = pickupPriority)
|
||||
pickupItem.struct
|
||||
},
|
||||
"create_itemstack" to { params ->
|
||||
}
|
||||
functions["create_itemstack"] = { params ->
|
||||
val itemId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
try {
|
||||
val item = BuiltInRegistries.ITEM.get(itemId)
|
||||
@@ -305,7 +311,7 @@ object GeneralMoLangFunctions {
|
||||
LOGGER.error("Cannot create item stack from identifier: $itemId, item with that ID does not exist. Possible typo?")
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
},
|
||||
"file" to { MoLangLoadedFilesCache.struct }
|
||||
)
|
||||
}
|
||||
functions["file"] = { MoLangLoadedFilesCache.struct }
|
||||
}
|
||||
}
|
||||
|
||||
+22
-21
@@ -26,43 +26,44 @@ object ItemStackMoLangFunctions : AbstractMoLangFunctionHolder<Pair<ItemStack, R
|
||||
val registryAccess = this.second
|
||||
val itemRegistry = registryAccess.registryOrThrow(Registries.ITEM)
|
||||
val holder = itemRegistry.wrapAsHolder(stack.item)
|
||||
val functions = mutableMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
map["item"] = { _ -> holder.asMoLangValue(Registries.ITEM) }
|
||||
map["count"] = { _ -> DoubleValue(stack.count.toDouble()) }
|
||||
map["damage_value"] = { _ -> DoubleValue(stack.damageValue) }
|
||||
map["max_damage"] = { _ -> DoubleValue(stack.maxDamage) }
|
||||
map["is_empty"] = { _ -> DoubleValue(stack.isEmpty) }
|
||||
map["shrink"] = { params -> stack.shrink(params.getInt(0)) }
|
||||
map["grow"] = { params -> stack.grow(params.getInt(0)) }
|
||||
map["is_of"] = { params ->
|
||||
functions["item"] = { holder.asMoLangValue(Registries.ITEM) }
|
||||
functions["count"] = { DoubleValue(stack.count.toDouble()) }
|
||||
functions["damage_value"] = { DoubleValue(stack.damageValue) }
|
||||
functions["max_damage"] = { DoubleValue(stack.maxDamage) }
|
||||
functions["is_empty"] = { DoubleValue(stack.isEmpty) }
|
||||
functions["shrink"] = { params -> stack.shrink(params.getInt(0)) }
|
||||
functions["grow"] = { params -> stack.grow(params.getInt(0)) }
|
||||
functions["is_of"] = { params ->
|
||||
DoubleValue(
|
||||
holder.`is`(
|
||||
params.getString(0).asIdentifierDefaultingNamespace()
|
||||
)
|
||||
params.getString(0).asIdentifierDefaultingNamespace(),
|
||||
),
|
||||
)
|
||||
}
|
||||
map["is_in"] = { params ->
|
||||
functions["is_in"] = { params ->
|
||||
DoubleValue(
|
||||
holder.`is`(
|
||||
TagKey.create(
|
||||
Registries.ITEM,
|
||||
params.getString(0).replace("#", "").asIdentifierDefaultingNamespace()
|
||||
)
|
||||
)
|
||||
params.getString(0).replace("#", "").asIdentifierDefaultingNamespace(),
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
map["is_food"] = { _ -> DoubleValue(stack.has(DataComponents.FOOD)) }
|
||||
map["is_enchanted"] = { _ -> DoubleValue(stack.hasFoil()) }
|
||||
map["has_enchantment"] = put@{ params ->
|
||||
functions["is_food"] = { DoubleValue(stack.has(DataComponents.FOOD)) }
|
||||
functions["is_enchanted"] = { DoubleValue(stack.hasFoil()) }
|
||||
functions["has_enchantment"] = hasEnchantment@{ params ->
|
||||
val enchantmentId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val minLevel = params.getIntOrNull(1) ?: 1
|
||||
val enchantmentRegistry = registryAccess.registryOrThrow(Registries.ENCHANTMENT)
|
||||
val holder =
|
||||
enchantmentRegistry.getHolder(enchantmentId).orElse(null) ?: return@put DoubleValue.ZERO
|
||||
enchantmentRegistry.getHolder(enchantmentId).orElse(null) ?: return@hasEnchantment DoubleValue.ZERO
|
||||
val level = EnchantmentHelper.getItemEnchantmentLevel(holder, stack)
|
||||
return@put DoubleValue(level >= minLevel)
|
||||
DoubleValue(level >= minLevel)
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+154
-137
@@ -53,26 +53,27 @@ import java.util.*
|
||||
object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>() {
|
||||
override fun LivingEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val entity = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["is_player"] = { _ -> DoubleValue(entity is Player) }
|
||||
map["is_npc"] = { _ -> DoubleValue(entity is NPCEntity) }
|
||||
map["is_mob"] = { _ -> DoubleValue(entity is Mob) }
|
||||
map["is_pokemon"] = { _ -> DoubleValue(entity is PokemonEntity) }
|
||||
map["is_animal"] = { _ -> DoubleValue(entity is Animal) }
|
||||
map["is_tamable"] = { _ -> DoubleValue(entity is TamableAnimal) }
|
||||
map["is_tamed"] = { _ -> DoubleValue(entity is TamableAnimal && entity.isTame) }
|
||||
map["is_hostile"] = { _ -> DoubleValue(entity is Monster) }
|
||||
map["is_baby"] = { _ -> DoubleValue(entity.isBaby) }
|
||||
map["is_adult"] = { _ -> DoubleValue(!entity.isBaby) }
|
||||
map["remove_effect"] = put@{ params ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["is_player"] = { DoubleValue(entity is Player) }
|
||||
functions["is_npc"] = { DoubleValue(entity is NPCEntity) }
|
||||
functions["is_mob"] = { DoubleValue(entity is Mob) }
|
||||
functions["is_pokemon"] = { DoubleValue(entity is PokemonEntity) }
|
||||
functions["is_animal"] = { DoubleValue(entity is Animal) }
|
||||
functions["is_tamable"] = { DoubleValue(entity is TamableAnimal) }
|
||||
functions["is_tamed"] = { DoubleValue(entity is TamableAnimal && entity.isTame) }
|
||||
functions["is_hostile"] = { DoubleValue(entity is Monster) }
|
||||
functions["is_baby"] = { DoubleValue(entity.isBaby) }
|
||||
functions["is_adult"] = { DoubleValue(!entity.isBaby) }
|
||||
functions["remove_effect"] = removeEffect@{ params ->
|
||||
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
|
||||
if (effectHolder != null) {
|
||||
entity.removeEffect(effectHolder)
|
||||
return@put DoubleValue.ONE
|
||||
return@removeEffect DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
map["add_effect"] = put@{ params ->
|
||||
functions["add_effect"] = addEffect@{ params ->
|
||||
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val duration = params.getInt(1)
|
||||
val amplifier = params.getIntOrNull(2) ?: 0
|
||||
@@ -81,36 +82,37 @@ object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>(
|
||||
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
|
||||
if (effectHolder != null) {
|
||||
entity.addEffect(MobEffectInstance(effectHolder, duration, amplifier, ambient, visible))
|
||||
return@put DoubleValue.ONE
|
||||
return@addEffect DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
map["has_effect"] = put@{ params ->
|
||||
functions["has_effect"] = { params ->
|
||||
val effectId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val effectHolder = BuiltInRegistries.MOB_EFFECT.getHolder(effectId).orElse(null)
|
||||
return@put DoubleValue(if (effectHolder != null) entity.hasEffect(effectHolder) else false)
|
||||
DoubleValue(effectHolder != null && entity.hasEffect(effectHolder))
|
||||
}
|
||||
map["heal"] = { params ->
|
||||
functions["heal"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
entity.heal(amount.toFloat())
|
||||
}
|
||||
map["is_looking_at"] = put@{ params ->
|
||||
functions["is_looking_at"] = isLookingAt@{ params ->
|
||||
val targetEntity = params.get<MoValue>(0)
|
||||
val maxDistance = params.getDoubleOrNull(1)?.toFloat() ?: 2.0F
|
||||
|
||||
val entity = if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
|
||||
targetEntity.obj as Entity
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
val entity =
|
||||
if (targetEntity is ObjectValue<*> && targetEntity.obj is Entity) {
|
||||
targetEntity.obj as Entity
|
||||
} else {
|
||||
return@isLookingAt DoubleValue.ZERO
|
||||
}
|
||||
|
||||
return@put DoubleValue(entity.isLookingAt(entity, maxDistance))
|
||||
DoubleValue(entity.isLookingAt(entity, maxDistance))
|
||||
}
|
||||
map["is_living_entity"] = { DoubleValue.ONE }
|
||||
map["is_flying"] = { _ -> DoubleValue(entity.isFallFlying) }
|
||||
map["is_sleeping"] = { _ -> DoubleValue(entity.isSleeping) }
|
||||
map["health"] = { _ -> DoubleValue(entity.health) }
|
||||
map["max_health"] = { _ -> DoubleValue(entity.maxHealth) }
|
||||
map["look_at_position"] = { params ->
|
||||
functions["is_living_entity"] = { DoubleValue.ONE }
|
||||
functions["is_flying"] = { DoubleValue(entity.isFallFlying) }
|
||||
functions["is_sleeping"] = { DoubleValue(entity.isSleeping) }
|
||||
functions["health"] = { DoubleValue(entity.health) }
|
||||
functions["max_health"] = { DoubleValue(entity.maxHealth) }
|
||||
functions["look_at_position"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
@@ -120,159 +122,174 @@ object LivingEntityMoLangFunctions : AbstractMoLangFunctionHolder<LivingEntity>(
|
||||
brain.setMemoryWithExpiry(
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()),
|
||||
duration.toLong()
|
||||
duration.toLong(),
|
||||
)
|
||||
}
|
||||
map["get_wander_control_memory"] = put@{
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
functions["get_wander_control_memory"] = {
|
||||
val value =
|
||||
entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
if (
|
||||
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT)
|
||||
&& entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
|
||||
!entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.VALUE_PRESENT) &&
|
||||
entity.brain.checkMemory(CobblemonMemories.WANDER_CONTROL, MemoryStatus.REGISTERED)
|
||||
) {
|
||||
entity.brain.setMemory(CobblemonMemories.WANDER_CONTROL, value)
|
||||
}
|
||||
return@put value.struct
|
||||
value.struct
|
||||
}
|
||||
map["get_position_memory"] = put@{ params ->
|
||||
functions["get_position_memory"] = getPosMemory@{ params ->
|
||||
val id = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
|
||||
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
|
||||
return@put when (val memory = entity.brain.getMemory(memoryType).get()) {
|
||||
is Vec3i -> VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x),
|
||||
"y" to DoubleValue(memory.y),
|
||||
"z" to DoubleValue(memory.z)
|
||||
return@getPosMemory when (val memory = entity.brain.getMemory(memoryType).get()) {
|
||||
is Vec3i -> {
|
||||
VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x),
|
||||
"y" to DoubleValue(memory.y),
|
||||
"z" to DoubleValue(memory.z),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
is Position -> VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x()),
|
||||
"y" to DoubleValue(memory.y()),
|
||||
"z" to DoubleValue(memory.z())
|
||||
is Position -> {
|
||||
VariableStruct(
|
||||
mapOf(
|
||||
"x" to DoubleValue(memory.x()),
|
||||
"y" to DoubleValue(memory.y()),
|
||||
"z" to DoubleValue(memory.z()),
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> DoubleValue.ZERO
|
||||
else -> {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["set_uuid_memory"] = put@{ params ->
|
||||
val memory = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
.let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
|
||||
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
|
||||
functions["set_uuid_memory"] = setUuidMemory@{ params ->
|
||||
val memory =
|
||||
params
|
||||
.getString(0)
|
||||
.asIdentifierDefaultingNamespace()
|
||||
.let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
|
||||
val uuid = params.getString(1).asUUID ?: return@setUuidMemory DoubleValue.ZERO
|
||||
val expiry = params.getIntOrNull(2) ?: -1
|
||||
if (expiry != -1) {
|
||||
entity.brain.setMemoryWithExpiry(memory, uuid, expiry.toLong())
|
||||
} else {
|
||||
entity.brain.setMemory(memory, uuid)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["erase_memory"] = put@{ params ->
|
||||
val memories = params.params.map { it.asString().asIdentifierDefaultingNamespace() }
|
||||
.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
functions["erase_memory"] = { params ->
|
||||
val memories =
|
||||
params.params
|
||||
.map { it.asString().asIdentifierDefaultingNamespace() }
|
||||
.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
memories.forEach { entity.brain.eraseMemory(it) }
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["has_memory_value"] = put@{ params ->
|
||||
functions["has_memory_value"] = hasMemoryValue@{ params ->
|
||||
for (param in params.params) {
|
||||
if (!entity.hasMemoryFromString(param.asString())) {
|
||||
return@put DoubleValue.ZERO
|
||||
return@hasMemoryValue DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["lacks_memory_value"] = put@{ params ->
|
||||
functions["lacks_memory_value"] = lacksMemoryValue@{ params ->
|
||||
for (param in params.params) {
|
||||
if (entity.hasMemoryFromString(param.asString())) {
|
||||
return@put DoubleValue.ZERO
|
||||
return@lacksMemoryValue DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["is_doing_activity"] = put@{ params ->
|
||||
functions["is_doing_activity"] = isDoingActivity@{ params ->
|
||||
for (param in params.params) {
|
||||
if (entity.isDoingActivityFromString(param.asString())) {
|
||||
return@put DoubleValue.ONE
|
||||
return@isDoingActivity DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["can_see"] = put@{ params ->
|
||||
functions["can_see"] = canSee@{ params ->
|
||||
val target = params.get<MoValue>(0)
|
||||
val range = params.getDoubleOrNull(1) ?: 32.0
|
||||
if (target is ObjectValue<*>) {
|
||||
val targetEntity = target.obj as? LivingEntity ?: return@put DoubleValue.ZERO
|
||||
val targetEntity = target.obj as? LivingEntity ?: return@canSee DoubleValue.ZERO
|
||||
val vector = targetEntity.eyePosition.subtract(entity.eyePosition).normalize()
|
||||
val trace = entity.traceEntityCollision(
|
||||
maxDistance = range.toFloat(),
|
||||
stepDistance = 0.1F,
|
||||
direction = vector,
|
||||
entityClass = targetEntity.javaClass,
|
||||
collideBlock = ClipContext.Fluid.NONE
|
||||
)
|
||||
return@put DoubleValue(targetEntity in (trace?.entities ?: emptyList()))
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
val trace =
|
||||
entity.traceEntityCollision(
|
||||
maxDistance = range.toFloat(),
|
||||
stepDistance = 0.1F,
|
||||
direction = vector,
|
||||
entityClass = targetEntity.javaClass,
|
||||
collideBlock = ClipContext.Fluid.NONE,
|
||||
)
|
||||
return@canSee DoubleValue(targetEntity in (trace?.entities ?: emptyList()))
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
|
||||
when (entity) {
|
||||
is PathfinderMob -> {
|
||||
functions["walk_to"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
|
||||
|
||||
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1),
|
||||
)
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
BlockPosTracker(Vec3(x, y + entity.eyeHeight, z)),
|
||||
)
|
||||
} else {
|
||||
entity.navigation.moveTo(x, y, z, speedMultiplier)
|
||||
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
|
||||
}
|
||||
}
|
||||
|
||||
functions["has_walk_target"] = {
|
||||
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
}
|
||||
|
||||
functions["set_can_float"] = { params ->
|
||||
val canFloat = params.getBooleanOrNull(0) != false
|
||||
entity.navigation.setCanFloat(canFloat)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
functions["get_pathfinding_malus"] = getPfMalus@{ params ->
|
||||
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
|
||||
if (type != null) {
|
||||
return@getPfMalus DoubleValue(entity.getPathfindingMalus(type))
|
||||
}
|
||||
Cobblemon.LOGGER.error("Cannot get pathfinding type, unknown value: ${params.getString(0)}")
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
functions["set_pathfinding_malus"] = setPfMalus@{ params ->
|
||||
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
|
||||
val malus = params.getDouble(1).toFloat()
|
||||
if (type != null) {
|
||||
entity.setPathfindingMalus(type, malus)
|
||||
return@setPfMalus DoubleValue.ONE
|
||||
}
|
||||
Cobblemon.LOGGER.error("Cannot set pathfinding type, unknown value: ${params.getString(0)}")
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (entity is PathfinderMob) {
|
||||
map["walk_to"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val speedMultiplier = params.getDoubleOrNull(3) ?: 0.35
|
||||
|
||||
if (entity.brain.checkMemory(MemoryModuleType.WALK_TARGET, MemoryStatus.REGISTERED)) {
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.WALK_TARGET,
|
||||
WalkTarget(Vec3(x, y, z), speedMultiplier.toFloat(), 1)
|
||||
)
|
||||
entity.brain.setMemory(
|
||||
MemoryModuleType.LOOK_TARGET,
|
||||
BlockPosTracker(Vec3(x, y + entity.eyeHeight, z))
|
||||
)
|
||||
} else {
|
||||
entity.navigation.moveTo(x, y, z, speedMultiplier)
|
||||
entity.lookControl.setLookAt(Vec3(x, y + entity.eyeHeight, z))
|
||||
}
|
||||
}
|
||||
|
||||
map["has_walk_target"] = { _ ->
|
||||
DoubleValue(entity.brain.getMemorySafely(MemoryModuleType.WALK_TARGET).isPresent || entity.isPathFinding)
|
||||
}
|
||||
|
||||
map["set_can_float"] = { params ->
|
||||
val canFloat = params.getBooleanOrNull(0) != false
|
||||
entity.navigation.setCanFloat(canFloat)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
map["get_pathfinding_malus"] = put@{ params ->
|
||||
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
|
||||
if (type != null) {
|
||||
return@put DoubleValue(entity.getPathfindingMalus(type))
|
||||
}
|
||||
Cobblemon.LOGGER.error("Unknown pathfinding type: ${params.getString(0)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["set_pathfinding_malus"] = put@{ params ->
|
||||
val type = PathType.entries.find { it.name == params.getString(0).uppercase() }
|
||||
val malus = params.getDouble(1).toFloat()
|
||||
if (type != null) {
|
||||
entity.setPathfindingMalus(type, malus)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
Cobblemon.LOGGER.error("Unknown pathfinding type: ${params.getString(0)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+108
-98
@@ -50,18 +50,23 @@ import kotlin.collections.any
|
||||
object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
|
||||
override fun NPCEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val npc = this
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
map["class"] = { StringValue(npc.npc.id.toString()) }
|
||||
map["name"] = { StringValue(npc.name.string) }
|
||||
map["level"] = { DoubleValue(npc.level) }
|
||||
map["has_aspect"] = { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
|
||||
map["in_battle"] = { DoubleValue(npc.isInBattle()) }
|
||||
map["battles"] = {
|
||||
ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }
|
||||
.mapIndexed { index, value -> "$index" to value }.toMap())
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["class"] = { StringValue(npc.npc.id.toString()) }
|
||||
functions["name"] = { StringValue(npc.name.string) }
|
||||
functions["level"] = { DoubleValue(npc.level) }
|
||||
functions["has_aspect"] = { params -> DoubleValue(npc.aspects.contains(params.getString(0))) }
|
||||
functions["in_battle"] = { DoubleValue(npc.isInBattle()) }
|
||||
functions["battles"] = {
|
||||
ArrayStruct(
|
||||
npc.battleIds
|
||||
.mapNotNull { BattleRegistry.getBattle(it)?.struct }
|
||||
.mapIndexed { index, value -> "$index" to value }
|
||||
.toMap(),
|
||||
)
|
||||
}
|
||||
map["stop_battles"] = { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
map["run_script_on_client"] = { params ->
|
||||
functions["stop_battles"] = { npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
functions["run_script_on_client"] = { params ->
|
||||
val world = npc.level()
|
||||
if (world is ServerLevel) {
|
||||
val script = params.getString(0)
|
||||
@@ -70,141 +75,142 @@ object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
|
||||
}
|
||||
Unit
|
||||
}
|
||||
map["run_script"] = { params ->
|
||||
functions["run_script"] = { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val runtime = MoLangRuntime()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
CobblemonScripts.run(script, runtime) ?: DoubleValue(0)
|
||||
}
|
||||
map["set_movable"] = put@{ params ->
|
||||
functions["set_movable"] = { params ->
|
||||
val movable = params.getBooleanOrNull(0) != false
|
||||
npc.isMovable = movable
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_invulnerable"] = put@{ params ->
|
||||
functions["set_invulnerable"] = { params ->
|
||||
val invulnerable = params.getBooleanOrNull(0) != false
|
||||
npc.isInvulnerable = invulnerable
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_leashable"] = put@{ params ->
|
||||
functions["set_leashable"] = { params ->
|
||||
val leashable = params.getBooleanOrNull(0) != false
|
||||
npc.isLeashable = leashable
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_allow_projectile_hits"] = put@{ params ->
|
||||
functions["set_allow_projectile_hits"] = { params ->
|
||||
val allowProjectileHits = params.getBooleanOrNull(0) != false
|
||||
npc.allowProjectileHits = allowProjectileHits
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_name_tag_visible"] = put@{ params ->
|
||||
functions["set_name_tag_visible"] = { params ->
|
||||
val nameTagVisible = params.getBooleanOrNull(0) != false
|
||||
npc.hideNameTag = !nameTagVisible
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["unset_interaction"] = put@{
|
||||
functions["unset_interaction"] = {
|
||||
npc.interaction = null
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_dialogue_interaction"] = put@{ params ->
|
||||
functions["set_dialogue_interaction"] = { params ->
|
||||
val dialogue = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
npc.interaction = DialogueNPCInteractionConfiguration().also {
|
||||
it.dialogue = dialogue
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
npc.interaction =
|
||||
DialogueNPCInteractionConfiguration().also {
|
||||
it.dialogue = dialogue
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_script_interaction"] = put@{ params ->
|
||||
functions["set_script_interaction"] = { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
npc.interaction = ScriptNPCInteractionConfiguration().also {
|
||||
it.script = script
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
npc.interaction =
|
||||
ScriptNPCInteractionConfiguration().also {
|
||||
it.script = script
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_player_texture"] = put@{ params ->
|
||||
functions["set_player_texture"] = setPlayerTexture@{ params ->
|
||||
val username = params.getString(0)
|
||||
// Re-applying it would be unnecessarily laggy.
|
||||
if (username == npc.data.map["player_texture_username"]?.asString()) {
|
||||
return@put DoubleValue.ZERO
|
||||
return@setPlayerTexture DoubleValue.ZERO
|
||||
}
|
||||
npc.loadTextureFromGameProfileName(username)
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["unset_player_texture"] = put@{
|
||||
functions["unset_player_texture"] = {
|
||||
npc.unloadTexture()
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_resource_identifier"] = put@{ params ->
|
||||
functions["set_resource_identifier"] = { params ->
|
||||
val identifier = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
npc.forcedResourceIdentifier = identifier
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["unset_resource_identifier"] = put@{
|
||||
functions["unset_resource_identifier"] = {
|
||||
npc.forcedResourceIdentifier = null
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_class"] = put@{ params ->
|
||||
functions["set_class"] = setClass@{ params ->
|
||||
val identifier = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val npcClass = NPCClasses.getByIdentifier(identifier)
|
||||
if (npcClass != null) {
|
||||
npc.npc = npcClass
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
Cobblemon.LOGGER.error("Unknown NPC class: $identifier")
|
||||
return@put DoubleValue.ZERO
|
||||
return@setClass DoubleValue.ONE
|
||||
}
|
||||
Cobblemon.LOGGER.error("Unknown NPC class: $identifier")
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["set_render_scale"] = put@{ params ->
|
||||
functions["set_render_scale"] = { params ->
|
||||
val scale = params.getDouble(0)
|
||||
npc.renderScale = scale.toFloat()
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["render_scale"] = { _ -> DoubleValue(npc.renderScale) }
|
||||
map["set_hitbox_scale"] = put@{ params ->
|
||||
functions["render_scale"] = { DoubleValue(npc.renderScale) }
|
||||
functions["set_hitbox_scale"] = { params ->
|
||||
val scale = params.getDouble(0).toFloat()
|
||||
npc.hitboxScale = scale
|
||||
npc.refreshDimensions()
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["hitbox_scale"] = { _ -> DoubleValue(npc.hitboxScale) }
|
||||
map["set_hitbox"] = put@{ params ->
|
||||
functions["hitbox_scale"] = { DoubleValue(npc.hitboxScale) }
|
||||
functions["set_hitbox"] = setHitbox@{ params ->
|
||||
if (params.params.isEmpty()) {
|
||||
npc.hitbox = null
|
||||
return@put DoubleValue.ONE
|
||||
return@setHitbox DoubleValue.ONE
|
||||
}
|
||||
val width = params.getDouble(0).toFloat()
|
||||
val height = params.getDouble(1).toFloat()
|
||||
val eyeHeight = params.getDoubleOrNull(2)?.toFloat() ?: (height * 0.85F)
|
||||
npc.hitbox = EntityDimensions.scalable(width, height).withEyeHeight(eyeHeight)
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["unset_hitbox"] = put@{
|
||||
functions["unset_hitbox"] = {
|
||||
npc.hitbox = null
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["aspects"] = put@{
|
||||
functions["aspects"] = {
|
||||
val aspects = npc.aspects
|
||||
return@put aspects.asArrayValue { StringValue(it) }
|
||||
aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map["add_aspect"] = put@{ params ->
|
||||
functions["add_aspect"] = { params ->
|
||||
val aspects = params.params.map { it.asString() }
|
||||
npc.appliedAspects.addAll(aspects)
|
||||
npc.updateAspects()
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["remove_aspect"] = put@{ params ->
|
||||
functions["remove_aspect"] = { params ->
|
||||
val aspects = params.params.map { it.asString() }
|
||||
npc.appliedAspects.removeAll(aspects.toSet())
|
||||
npc.updateAspects()
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["party"] = put@{
|
||||
val party = npc.party ?: return@put DoubleValue.ZERO
|
||||
return@put party.asMoLangValue()
|
||||
functions["party"] = party@{
|
||||
val party = npc.party ?: return@party DoubleValue.ZERO
|
||||
party.asMoLangValue()
|
||||
}
|
||||
map["create_npc_party"] = put@{
|
||||
functions["create_npc_party"] = {
|
||||
val party = NPCPartyStore(npc)
|
||||
return@put party.asMoLangValue()
|
||||
party.asMoLangValue()
|
||||
}
|
||||
map["set_npc_party"] = { params ->
|
||||
functions["set_npc_party"] = { params ->
|
||||
val party = params.get<MoValue>(0)
|
||||
if (party is ObjectValue<*>) {
|
||||
npc.party = party.obj as NPCPartyStore
|
||||
@@ -213,18 +219,19 @@ object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["run_action_effect"] = put@{ params ->
|
||||
functions["run_action_effect"] = runActionEffect@{ params ->
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
runtime.withNPCValue(value = npc)
|
||||
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
|
||||
if (actionEffect != null) {
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
providers = mutableListOf(NPCProvider(npc)),
|
||||
runtime = runtime,
|
||||
level = npc.level()
|
||||
)
|
||||
val context =
|
||||
ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
providers = mutableListOf(NPCProvider(npc)),
|
||||
runtime = runtime,
|
||||
level = npc.level(),
|
||||
)
|
||||
npc.actionEffect = context
|
||||
npc.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
|
||||
npc.brain.setActiveActivityIfPossible(CobblemonActivities.ACTION_EFFECT)
|
||||
@@ -236,37 +243,40 @@ object NPCMoLangFunctions : AbstractMoLangFunctionHolder<NPCEntity>() {
|
||||
}
|
||||
}
|
||||
|
||||
return@put DoubleValue.ONE
|
||||
return@runActionEffect DoubleValue.ONE
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["put_pokemon_in_healer"] = put@{ params ->
|
||||
val healer = npc.level()
|
||||
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
|
||||
.orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val party = npc.party ?: return@put DoubleValue.ZERO
|
||||
functions["put_pokemon_in_healer"] = putPokeInHealer@{ params ->
|
||||
val healer =
|
||||
npc
|
||||
.level()
|
||||
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
|
||||
.orElse(null) ?: return@putPokeInHealer DoubleValue.ZERO
|
||||
val party = npc.party ?: return@putPokeInHealer DoubleValue.ZERO
|
||||
if (healer.canHeal(party)) {
|
||||
healer.activate(npc.uuid, party)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
return@putPokeInHealer DoubleValue.ONE
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["look_at_position"] = { params ->
|
||||
functions["look_at_position"] = { params ->
|
||||
val pos = Vec3(params.getDouble(0), params.getDouble(1), params.getDouble(2))
|
||||
npc.lookAt(EntityAnchorArgument.Anchor.EYES, pos)
|
||||
}
|
||||
map["environment"] = { _ -> npc.runtime.environment }
|
||||
map["party"] = { npc.party?.struct ?: DoubleValue.ZERO }
|
||||
map["has_party"] = { DoubleValue(npc.party != null) }
|
||||
map["is_npc"] = { DoubleValue.ONE }
|
||||
map["can_battle"] =
|
||||
{ DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false) }
|
||||
map["set_battle_theme"] = put@{ params ->
|
||||
functions["environment"] = { npc.runtime.environment }
|
||||
functions["party"] = { npc.party?.struct ?: DoubleValue.ZERO }
|
||||
functions["has_party"] = { DoubleValue(npc.party != null) }
|
||||
functions["is_npc"] = { DoubleValue.ONE }
|
||||
functions["can_battle"] = {
|
||||
DoubleValue(npc.party?.any { it.currentHealth > 0 } == true || npc.npc.party?.isStatic == false)
|
||||
}
|
||||
functions["set_battle_theme"] = { params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
npc.npc.battleTheme = soundId
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+11
-11
@@ -23,40 +23,40 @@ import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
object PCMoLangFunctions : AbstractMoLangFunctionHolder<PCStore>() {
|
||||
override fun PCStore.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pc = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["get_pokemon"] = getPokemon@{ params ->
|
||||
functions["get_pokemon"] = getPokemon@{ params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = pc[PCPosition(box, slot)] ?: return@getPokemon DoubleValue.ZERO
|
||||
return@getPokemon pokemon.struct
|
||||
pokemon.struct
|
||||
}
|
||||
map["set_pokemon"] = { params ->
|
||||
functions["set_pokemon"] = { params ->
|
||||
val box = params.getInt(0)
|
||||
val slot = params.getInt(1)
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(2).obj
|
||||
pc[PCPosition(box, slot)] = pokemon
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["resize"] = { params ->
|
||||
functions["resize"] = { params ->
|
||||
val newSize = params.getInt(0)
|
||||
val lockNewSize = params.getBooleanOrNull(1) == true
|
||||
pc.resize(newSize, lockNewSize)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["get_box_count"] = { DoubleValue(pc.boxes.size.toDouble()) }
|
||||
map["has_unlocked_wallpaper"] = { params ->
|
||||
functions["get_box_count"] = { DoubleValue(pc.boxes.size.toDouble()) }
|
||||
functions["has_unlocked_wallpaper"] = { params ->
|
||||
val wallpaper = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
DoubleValue(pc.unlockedWallpapers.contains(wallpaper))
|
||||
}
|
||||
map["get_unlocked_wallpapers"] = { pc.unlockedWallpapers.asArrayValue { StringValue(it.toString()) } }
|
||||
map["unlock_wallpaper"] = unlockWallpaper@{
|
||||
functions["get_unlocked_wallpapers"] = { pc.unlockedWallpapers.asArrayValue { StringValue(it.toString()) } }
|
||||
functions["unlock_wallpaper"] = unlockWallpaper@{
|
||||
val wallpaper = it.getString(0).asIdentifierDefaultingNamespace()
|
||||
val playSound = it.getBooleanOrNull(1) != false
|
||||
CobblemonUnlockableWallpapers.unlockableWallpapers[wallpaper] ?: return@unlockWallpaper DoubleValue.ZERO
|
||||
return@unlockWallpaper DoubleValue(pc.unlockWallpaper(wallpaper, playSound))
|
||||
DoubleValue(pc.unlockWallpaper(wallpaper, playSound))
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+7
-5
@@ -14,21 +14,23 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyStore
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
|
||||
object PartyMoLangFunctions: AbstractMoLangFunctionHolder<PartyStore>() {
|
||||
object PartyMoLangFunctions : AbstractMoLangFunctionHolder<PartyStore>() {
|
||||
override fun PartyStore.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val party = this
|
||||
val map = hashMapOf<String, (MoParams) -> Any>()
|
||||
map["get_pokemon"] = getPokemon@{ params ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["get_pokemon"] = getPokemon@{ params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = party.get(index) ?: return@getPokemon DoubleValue.ZERO
|
||||
pokemon.struct
|
||||
}
|
||||
map["set_pokemon"] = { params ->
|
||||
functions["set_pokemon"] = { params ->
|
||||
val index = params.getInt(0)
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(1).obj
|
||||
party.set(index, pokemon)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+300
-266
@@ -17,12 +17,12 @@ import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.bedrockk.molang.runtime.value.StringValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.api.dialogue.PlayerDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.api.tms.TechnicalMachines
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
@@ -52,355 +52,389 @@ import net.minecraft.core.registries.Registries
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import net.minecraft.sounds.SoundSource
|
||||
import net.minecraft.stats.Stats
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.ItemStack
|
||||
import net.minecraft.world.level.GameType
|
||||
import kotlin.collections.iterator
|
||||
|
||||
object PlayerMoLangFunctions : AbstractMoLangFunctionHolder<Player>() {
|
||||
override fun Player.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val player = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["username"] = { _ -> StringValue(player.gameProfile.name) }
|
||||
map["uuid"] = { _ -> StringValue(player.gameProfile.id.toString()) }
|
||||
map["main_held_item"] = { _ -> player.mainHandItem.asMoLangValue(player.registryAccess()) }
|
||||
map["off_held_item"] = { _ -> player.offhandItem.asMoLangValue(player.registryAccess()) }
|
||||
map["inventory"] = put@{ _ ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["username"] = { StringValue(player.gameProfile.name) }
|
||||
functions["uuid"] = { StringValue(player.gameProfile.id.toString()) }
|
||||
functions["main_held_item"] = { player.mainHandItem.asMoLangValue(player.registryAccess()) }
|
||||
functions["off_held_item"] = { player.offhandItem.asMoLangValue(player.registryAccess()) }
|
||||
functions["inventory"] = {
|
||||
val inventory = player.inventory
|
||||
val items = ArrayStruct(hashMapOf())
|
||||
for (i in 0 until inventory.containerSize) {
|
||||
items.setDirectly("$i", inventory.getItem(i).asMoLangValue(player.registryAccess()))
|
||||
}
|
||||
return@put items
|
||||
items
|
||||
}
|
||||
map["has_inventory_space"] = put@{ _ ->
|
||||
functions["has_inventory_space"] = {
|
||||
val inventory = player.inventory
|
||||
return@put if (inventory.getFreeSlot() != -1) DoubleValue.ONE else DoubleValue.ZERO
|
||||
if (inventory.freeSlot != -1) DoubleValue.ONE else DoubleValue.ZERO
|
||||
}
|
||||
map["set_inventory_slot"] = put@{ params ->
|
||||
functions["set_inventory_slot"] = setInvSlot@{ params ->
|
||||
val slot = params.getInt(0)
|
||||
val inventory = player.inventory
|
||||
if (slot !in 0 until inventory.containerSize) return@put DoubleValue.ZERO
|
||||
if (slot !in 0 until inventory.containerSize) return@setInvSlot DoubleValue.ZERO
|
||||
|
||||
val value = params.getOrNull<MoValue>(1) ?: return@put DoubleValue.ZERO
|
||||
val value = params.getOrNull<MoValue>(1) ?: return@setInvSlot DoubleValue.ZERO
|
||||
|
||||
val stack: ItemStack? = when (value) {
|
||||
is ObjectValue<*> -> value.obj as? ItemStack
|
||||
is StringValue -> {
|
||||
val id = ResourceLocation.parse(value.value)
|
||||
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
|
||||
?: return@put DoubleValue.ZERO
|
||||
ItemStack(item)
|
||||
val stack: ItemStack? =
|
||||
when (value) {
|
||||
is ObjectValue<*> -> {
|
||||
value.obj as? ItemStack
|
||||
}
|
||||
|
||||
is StringValue -> {
|
||||
val id = ResourceLocation.parse(value.value)
|
||||
val item =
|
||||
player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
|
||||
?: return@setInvSlot DoubleValue.ZERO
|
||||
ItemStack(item)
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (stack == null) return@put DoubleValue.ZERO
|
||||
if (stack == null) return@setInvSlot DoubleValue.ZERO
|
||||
|
||||
inventory.setItem(slot, stack)
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["give_item"] = put@{ params ->
|
||||
functions["give_item"] = giveItem@{ params ->
|
||||
val inventory = player.inventory
|
||||
val freeSlot = inventory.getFreeSlot()
|
||||
val value = params.getOrNull<MoValue>(0) ?: return@put DoubleValue.ZERO
|
||||
val freeSlot = inventory.freeSlot
|
||||
val value = params.getOrNull<MoValue>(0) ?: return@giveItem DoubleValue.ZERO
|
||||
val dropIfFull = params.getBooleanOrNull(1) ?: false
|
||||
|
||||
val stack: ItemStack? = when (value) {
|
||||
is ObjectValue<*> -> value.obj as? ItemStack
|
||||
is StringValue -> {
|
||||
val id = ResourceLocation.parse(value.value)
|
||||
val item = player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
|
||||
?: return@put DoubleValue.ZERO
|
||||
ItemStack(item)
|
||||
val stack: ItemStack? =
|
||||
when (value) {
|
||||
is ObjectValue<*> -> {
|
||||
value.obj as? ItemStack
|
||||
}
|
||||
|
||||
is StringValue -> {
|
||||
val id = ResourceLocation.parse(value.value)
|
||||
val item =
|
||||
player.registryAccess().registryOrThrow(Registries.ITEM).get(id)
|
||||
?: return@giveItem DoubleValue.ZERO
|
||||
ItemStack(item)
|
||||
}
|
||||
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
if (stack == null) return@put DoubleValue.ZERO
|
||||
if (stack == null) return@giveItem DoubleValue.ZERO
|
||||
|
||||
if (freeSlot == -1) {
|
||||
if (dropIfFull) {
|
||||
player.drop(stack, false)
|
||||
return@put DoubleValue.ONE
|
||||
return@giveItem DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
return@giveItem DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setItem(freeSlot, stack)
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["face"] = { params ->
|
||||
functions["face"] = { params ->
|
||||
ObjectValue(
|
||||
PlayerDialogueFaceProvider(
|
||||
player.uuid,
|
||||
params.getBooleanOrNull(0) != false
|
||||
)
|
||||
params.getBooleanOrNull(0) != false,
|
||||
),
|
||||
)
|
||||
}
|
||||
map["swing_hand"] = { _ -> player.swing(player.usedItemHand) }
|
||||
map["food_level"] = { _ -> DoubleValue(player.foodData.foodLevel) }
|
||||
map["saturation_level"] = { _ -> DoubleValue(player.foodData.saturationLevel) }
|
||||
map["tell"] = { params ->
|
||||
functions["swing_hand"] = { player.swing(player.usedItemHand) }
|
||||
functions["food_level"] = { DoubleValue(player.foodData.foodLevel) }
|
||||
functions["saturation_level"] = { DoubleValue(player.foodData.saturationLevel) }
|
||||
functions["tell"] = { params ->
|
||||
val message = params.getString(0).text()
|
||||
val overlay = params.getBooleanOrNull(1) == true
|
||||
player.displayClientMessage(message, overlay)
|
||||
}
|
||||
map["teleport"] = { params ->
|
||||
functions["teleport"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val playParticleOptionss = params.getBooleanOrNull(3) ?: false
|
||||
player.randomTeleport(x, y, z, playParticleOptionss)
|
||||
val playParticleOptions = params.getBooleanOrNull(3) ?: false
|
||||
player.randomTeleport(x, y, z, playParticleOptions)
|
||||
}
|
||||
map["heal"] = { params ->
|
||||
functions["heal"] = { params ->
|
||||
val amount = params.getDoubleOrNull(0) ?: player.maxHealth
|
||||
player.heal(amount.toFloat())
|
||||
}
|
||||
map["environment"] = {
|
||||
functions["environment"] = {
|
||||
val environment = MoLangEnvironment()
|
||||
environment.query = player.asMoLangValue()
|
||||
environment
|
||||
}
|
||||
map["is_player"] = { DoubleValue.ONE }
|
||||
map["riding_pokemon"] = put@{
|
||||
functions["is_player"] = { DoubleValue.ONE }
|
||||
functions["riding_pokemon"] = ridingPokemon@{
|
||||
val vehicle = player.vehicle
|
||||
if (vehicle is PokemonEntity) {
|
||||
return@put vehicle.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
return@ridingPokemon vehicle.struct
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
if (player is ServerPlayer) {
|
||||
map["seen_credits"] = { _ ->
|
||||
DoubleValue(player.seenCredits)
|
||||
}
|
||||
map["is_in_dialogue"] = { _ ->
|
||||
DoubleValue(player.isInDialogue)
|
||||
}
|
||||
map["active_dialogue"] = put@{ _ ->
|
||||
if (player.isInDialogue) {
|
||||
return@put player.activeDialogue?.toMoLangStruct() ?: DoubleValue.ZERO
|
||||
} else {
|
||||
|
||||
when (player) {
|
||||
is ServerPlayer -> {
|
||||
functions["seen_credits"] = { DoubleValue(player.seenCredits) }
|
||||
functions["is_in_dialogue"] = { DoubleValue(player.isInDialogue) }
|
||||
functions["active_dialogue"] = activeDialogue@{
|
||||
if (player.isInDialogue) {
|
||||
return@activeDialogue player.activeDialogue?.toMoLangStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_spectator"] = { DoubleValue(player.isSpectator) }
|
||||
map["is_creative"] = { DoubleValue(player.isCreative) }
|
||||
map["is_survival"] = { DoubleValue(player.gameMode.isSurvival) }
|
||||
map["is_adventure"] = { DoubleValue(player.gameMode.gameModeForPlayer == GameType.ADVENTURE) }
|
||||
map["run_command"] = { params ->
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
map["set_battle_theme"] = put@{ params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["battle_music"] = put@{ params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
val volume = params.getDoubleOrNull(1)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val restart = params.getBooleanOrNull(3) ?: true
|
||||
functions["is_spectator"] = { DoubleValue(player.isSpectator) }
|
||||
functions["is_creative"] = { DoubleValue(player.isCreative) }
|
||||
functions["is_survival"] = { DoubleValue(player.gameMode.isSurvival) }
|
||||
functions["is_adventure"] = { DoubleValue(player.gameMode.gameModeForPlayer == GameType.ADVENTURE) }
|
||||
functions["run_command"] = { params ->
|
||||
val command = params.getString(0)
|
||||
player.server.commands.performPrefixedCommand(player.createCommandSourceStack(), command)
|
||||
}
|
||||
functions["set_battle_theme"] = { params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
Cobblemon.playerDataManager.getGenericData(player).battleTheme = soundId
|
||||
DoubleValue.ONE
|
||||
}
|
||||
functions["battle_music"] = battleMusic@{ params ->
|
||||
val soundId = params.getString(0).asResource()
|
||||
val volume = params.getDoubleOrNull(1)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val restart = params.getBooleanOrNull(3) ?: true
|
||||
|
||||
if (soundId != null) {
|
||||
val packet = BattleMusicPacket(soundId, volume, pitch, restart)
|
||||
if (soundId != null) {
|
||||
val packet = BattleMusicPacket(soundId, volume, pitch, restart)
|
||||
packet.sendToPlayer(player)
|
||||
return@battleMusic DoubleValue.ONE
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
functions["stop_battle_music"] = {
|
||||
val packet = BattleMusicPacket(null)
|
||||
packet.sendToPlayer(player)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
map["stop_battle_music"] = put@{ _ ->
|
||||
val packet = BattleMusicPacket(null)
|
||||
packet.sendToPlayer(player)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["play_sound_on_server"] = { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
|
||||
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
|
||||
functions["play_sound_on_server"] = { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = SoundSource.valueOf(params.getString(1).uppercase())
|
||||
val volume = params.getDoubleOrNull(2)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(3)?.toFloat() ?: 1.0f
|
||||
|
||||
val packet =
|
||||
UnvalidatedPlaySoundS2CPacket(sound, soundSource, player.x, player.y, player.z, volume, pitch)
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
map["is_party_at_full_health"] = { _ ->
|
||||
DoubleValue(player.party().none(Pokemon::canBeHealed))
|
||||
}
|
||||
map["can_heal_at_healer"] = put@{ params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = player.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
|
||||
?: return@put DoubleValue.ZERO
|
||||
val party = player.party()
|
||||
return@put DoubleValue(healer.canHeal(party))
|
||||
}
|
||||
map["put_pokemon_in_healer"] = put@{ params ->
|
||||
val healer = player.level()
|
||||
.getBlockEntity(params.get<ArrayStruct>(0).asBlockPos(), CobblemonBlockEntities.HEALING_MACHINE)
|
||||
.orElse(null) ?: return@put DoubleValue.ZERO
|
||||
val party = player.party()
|
||||
if (healer.canHeal(party)) {
|
||||
healer.activate(player.uuid, party)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
val packet =
|
||||
UnvalidatedPlaySoundS2CPacket(
|
||||
sound,
|
||||
soundSource,
|
||||
player.x,
|
||||
player.y,
|
||||
player.z,
|
||||
volume,
|
||||
pitch,
|
||||
)
|
||||
|
||||
packet.sendToPlayer(player)
|
||||
}
|
||||
}
|
||||
map["party"] = { player.party().struct }
|
||||
map["pc"] = { player.pc().struct }
|
||||
map["has_permission"] = { params ->
|
||||
DoubleValue(
|
||||
Cobblemon.permissionValidator.hasPermission(
|
||||
player,
|
||||
params.getString(0),
|
||||
params.getIntOrNull(1) ?: 4
|
||||
functions["is_party_at_full_health"] = { DoubleValue(player.party().none(Pokemon::canBeHealed)) }
|
||||
functions["can_heal_at_healer"] = canHealAtHealer@{ params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer =
|
||||
player.level().getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
|
||||
?: return@canHealAtHealer DoubleValue.ZERO
|
||||
val party = player.party()
|
||||
DoubleValue(healer.canHeal(party))
|
||||
}
|
||||
functions["put_pokemon_in_healer"] = putPokemonAtHealer@{ params ->
|
||||
val healer =
|
||||
player
|
||||
.level()
|
||||
.getBlockEntity(
|
||||
params.get<ArrayStruct>(0).asBlockPos(),
|
||||
CobblemonBlockEntities.HEALING_MACHINE,
|
||||
).orElse(null) ?: return@putPokemonAtHealer DoubleValue.ZERO
|
||||
val party = player.party()
|
||||
if (healer.canHeal(party)) {
|
||||
healer.activate(player.uuid, party)
|
||||
return@putPokemonAtHealer DoubleValue.ONE
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
functions["party"] = { player.party().struct }
|
||||
functions["pc"] = { player.pc().struct }
|
||||
functions["has_permission"] = { params ->
|
||||
DoubleValue(
|
||||
Cobblemon.permissionValidator.hasPermission(
|
||||
player,
|
||||
params.getString(0),
|
||||
params.getIntOrNull(1) ?: 4,
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
map["data"] = { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
|
||||
map["save_data"] = { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
|
||||
map["in_battle"] = { DoubleValue(player.isInBattle()) }
|
||||
map["battle"] = { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
map["get_npc_data"] = put@{ params ->
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
|
||||
?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put data.map[npcId]!!
|
||||
} else {
|
||||
}
|
||||
functions["data"] = { params -> Cobblemon.molangData.load(player.uuid, params.getStringOrNull(0)) }
|
||||
functions["save_data"] = { params -> Cobblemon.molangData.save(player.uuid, params.getStringOrNull(0)) }
|
||||
functions["in_battle"] = { DoubleValue(player.isInBattle()) }
|
||||
functions["battle"] = { player.getBattleState()?.first?.struct ?: DoubleValue.ZERO }
|
||||
functions["get_npc_data"] = getNpcData@{ params ->
|
||||
val npcId =
|
||||
((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
|
||||
?: params.getString(0)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(1))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@getNpcData data.map[npcId]!!
|
||||
}
|
||||
|
||||
val vars = VariableStruct()
|
||||
data.map[npcId] = vars
|
||||
return@put vars
|
||||
vars
|
||||
}
|
||||
}
|
||||
map["get_npc_variable"] = put@{ params ->
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
|
||||
?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@put (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_npc_variable"] = put@{ params ->
|
||||
val npcId = ((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
|
||||
?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val saveAfterwards = params.getBooleanOrNull(3) != false
|
||||
val path = params.getStringOrNull(4)
|
||||
val data = Cobblemon.molangData.load(player.uuid, path)
|
||||
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
|
||||
npcData.map[variable] = value
|
||||
if (saveAfterwards) {
|
||||
Cobblemon.molangData.save(player.uuid, path)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["pokedex"] = { player.pokedex().struct }
|
||||
map["has_advancement"] = put@{ params ->
|
||||
val requiredAdvancement = ResourceLocation.parse(params.getString(0))
|
||||
for (entry in player.advancements.progress) {
|
||||
if (entry.key.id == requiredAdvancement && entry.value.isDone) {
|
||||
return@put DoubleValue.ONE
|
||||
functions["get_npc_variable"] = getNpcVariable@{ params ->
|
||||
val npcId =
|
||||
((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
|
||||
?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val data = Cobblemon.molangData.load(player.uuid, params.getStringOrNull(2))
|
||||
if (data.map.containsKey(npcId)) {
|
||||
return@getNpcVariable (data.map[npcId] as VariableStruct).map[variable] ?: DoubleValue.ZERO
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
map["has_tm_move_unlocked"] = put@{ params ->
|
||||
val move = Moves.getByName(params.getString(0)) ?: return@put DoubleValue.ZERO
|
||||
val tm = TechnicalMachines.moveToTM[move] ?: return@put DoubleValue.ZERO
|
||||
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 1.0 else 0.0)
|
||||
}
|
||||
map["unlock_tm_move"] = put@{ params ->
|
||||
val move = Moves.getByName(params.getString(0)) ?: return@put DoubleValue.ZERO
|
||||
val tm = TechnicalMachines.moveToTM[move] ?: return@put DoubleValue.ZERO
|
||||
player.tmList().syncTMFromMove(move)
|
||||
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 1.0 else 0.0)
|
||||
}
|
||||
map["lock_tm_move"] = put@{ params ->
|
||||
val move = Moves.getByName(params.getString(0)) ?: return@put DoubleValue.ZERO
|
||||
val tm = TechnicalMachines.moveToTM[move] ?: return@put DoubleValue.ZERO
|
||||
player.tmList().syncTMLockFromMove(move)
|
||||
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 0.0 else 1.0)
|
||||
}
|
||||
map["start_battle"] = put@{ params ->
|
||||
val opponentValue = params.get<MoValue>(0)
|
||||
val opponent = if (opponentValue is ObjectValue<*>) {
|
||||
opponentValue.obj as ServerPlayer
|
||||
} else {
|
||||
val paramString = opponentValue.asString()
|
||||
val playerUUID = paramString.asUUID
|
||||
if (playerUUID != null) {
|
||||
server()?.playerList?.getPlayer(playerUUID) ?: return@put DoubleValue.ZERO
|
||||
} else {
|
||||
server()?.playerList?.getPlayerByName(paramString) ?: return@put DoubleValue.ZERO
|
||||
functions["set_npc_variable"] = { params ->
|
||||
val npcId =
|
||||
((params.get<MoValue>(0) as? ObjectValue<*>)?.obj as? NPCEntity)?.stringUUID
|
||||
?: params.getString(0)
|
||||
val variable = params.getString(1)
|
||||
val value = params.get<MoValue>(2)
|
||||
val saveAfterwards = params.getBooleanOrNull(3) != false
|
||||
val path = params.getStringOrNull(4)
|
||||
val data = Cobblemon.molangData.load(player.uuid, path)
|
||||
val npcData = data.map.getOrPut(npcId) { VariableStruct() } as VariableStruct
|
||||
npcData.map[variable] = value
|
||||
if (saveAfterwards) {
|
||||
Cobblemon.molangData.save(player.uuid, path)
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
val format = params.getStringOrNull(1)
|
||||
?.let(BattleFormat::fromFormatIdentifier)
|
||||
?: BattleFormat.GEN_9_SINGLES
|
||||
functions["pokedex"] = { player.pokedex().struct }
|
||||
functions["has_advancement"] = hasAdvancement@{ params ->
|
||||
val requiredAdvancement = ResourceLocation.parse(params.getString(0))
|
||||
player.advancements.progress.forEach { (holder, progress) ->
|
||||
if (holder.id == requiredAdvancement && progress.isDone) {
|
||||
return@hasAdvancement DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
functions["has_tm_move_unlocked"] = hasTmMoveUnlocked@{ params ->
|
||||
val move = Moves.getByName(params.getString(0)) ?: return@hasTmMoveUnlocked DoubleValue.ZERO
|
||||
val tm = TechnicalMachines.moveToTM[move] ?: return@hasTmMoveUnlocked DoubleValue.ZERO
|
||||
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 1.0 else 0.0)
|
||||
}
|
||||
functions["unlock_tm_move"] = unlockTmMove@{ params ->
|
||||
val move = Moves.getByName(params.getString(0)) ?: return@unlockTmMove DoubleValue.ZERO
|
||||
val tm = TechnicalMachines.moveToTM[move] ?: return@unlockTmMove DoubleValue.ZERO
|
||||
player.tmList().syncTMFromMove(move)
|
||||
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 1.0 else 0.0)
|
||||
}
|
||||
functions["lock_tm_move"] = lockTmMove@{ params ->
|
||||
val move = Moves.getByName(params.getString(0)) ?: return@lockTmMove DoubleValue.ZERO
|
||||
val tm = TechnicalMachines.moveToTM[move] ?: return@lockTmMove DoubleValue.ZERO
|
||||
player.tmList().syncTMLockFromMove(move)
|
||||
DoubleValue(if (player.tmList().learnedTMs.contains(tm.id)) 0.0 else 1.0)
|
||||
}
|
||||
functions["start_battle"] = startBattle@{ params ->
|
||||
val opponentValue = params.get<MoValue>(0)
|
||||
val opponent =
|
||||
if (opponentValue is ObjectValue<*>) {
|
||||
opponentValue.obj as ServerPlayer
|
||||
} else {
|
||||
val paramString = opponentValue.asString()
|
||||
val playerUUID = paramString.asUUID
|
||||
if (playerUUID != null) {
|
||||
server()?.playerList?.getPlayer(playerUUID) ?: return@startBattle DoubleValue.ZERO
|
||||
} else {
|
||||
server()?.playerList?.getPlayerByName(paramString)
|
||||
?: return@startBattle DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
val format =
|
||||
params
|
||||
.getStringOrNull(1)
|
||||
?.let(BattleFormat::fromFormatIdentifier)
|
||||
?: BattleFormat.GEN_9_SINGLES
|
||||
|
||||
val setLevel = params.getIntOrNull(2) ?: -1
|
||||
format.adjustLevel = setLevel
|
||||
val setLevel = params.getIntOrNull(2) ?: -1
|
||||
format.adjustLevel = setLevel
|
||||
|
||||
val rules = params.getStringOrNull(5)
|
||||
?.split(",")
|
||||
?.toSet()
|
||||
?: emptySet()
|
||||
val rules =
|
||||
params
|
||||
.getStringOrNull(5)
|
||||
?.split(",")
|
||||
?.toSet()
|
||||
?: emptySet()
|
||||
|
||||
val modifiedBattleFormat = BattleFormat.setBattleRules(
|
||||
battleFormat = format,
|
||||
rules = rules
|
||||
)
|
||||
val modifiedBattleFormat =
|
||||
BattleFormat.setBattleRules(
|
||||
battleFormat = format,
|
||||
rules = rules,
|
||||
)
|
||||
|
||||
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
|
||||
val healFirst = params.getBooleanOrNull(4) ?: false
|
||||
val cloneParties = (setLevel != -1) || (params.getBooleanOrNull(3) ?: false)
|
||||
val healFirst = params.getBooleanOrNull(4) ?: false
|
||||
|
||||
val battleStartResult = BattleBuilder.pvp1v1(
|
||||
player1 = player,
|
||||
player2 = opponent,
|
||||
battleFormat = modifiedBattleFormat,
|
||||
cloneParties = cloneParties,
|
||||
healFirst = healFirst
|
||||
)
|
||||
var returnValue: MoValue = DoubleValue.ZERO
|
||||
battleStartResult.ifSuccessful { returnValue = it.struct }
|
||||
return@put returnValue
|
||||
val battleStartResult =
|
||||
BattleBuilder.pvp1v1(
|
||||
player1 = player,
|
||||
player2 = opponent,
|
||||
battleFormat = modifiedBattleFormat,
|
||||
cloneParties = cloneParties,
|
||||
healFirst = healFirst,
|
||||
)
|
||||
var returnValue: MoValue = DoubleValue.ZERO
|
||||
battleStartResult.ifSuccessful { returnValue = it.struct }
|
||||
returnValue
|
||||
}
|
||||
functions["get_custom_stat"] = getCustomStat@{ params ->
|
||||
val statName = params.getString(0)
|
||||
|
||||
val resourceLocation = ResourceLocation.tryParse(statName) ?: return@getCustomStat DoubleValue.ZERO
|
||||
val statResourceLocation =
|
||||
BuiltInRegistries.CUSTOM_STAT.get(resourceLocation) ?: return@getCustomStat DoubleValue.ZERO
|
||||
|
||||
val exists = Stats.CUSTOM.contains(statResourceLocation)
|
||||
if (!exists) return@getCustomStat DoubleValue.ZERO
|
||||
|
||||
val stat = Stats.CUSTOM.get(statResourceLocation)
|
||||
val value = player.stats.getValue(stat)
|
||||
|
||||
DoubleValue(value)
|
||||
}
|
||||
functions["get_starter_uuid"] = {
|
||||
Cobblemon.playerDataManager.getGenericData(player).starterUUID?.let {
|
||||
StringValue(
|
||||
it.toString(),
|
||||
)
|
||||
} ?: DoubleValue.ZERO
|
||||
}
|
||||
functions["has_chosen_starter"] = {
|
||||
DoubleValue(Cobblemon.playerDataManager.getGenericData(player).starterSelected)
|
||||
}
|
||||
}
|
||||
map["get_custom_stat"] = put@{ params ->
|
||||
val statName = params.getString(0)
|
||||
|
||||
val resourceLocation = ResourceLocation.tryParse(statName) ?: return@put DoubleValue.ZERO
|
||||
val statResourceLocation =
|
||||
BuiltInRegistries.CUSTOM_STAT.get(resourceLocation) ?: return@put DoubleValue.ZERO
|
||||
|
||||
val exists = net.minecraft.stats.Stats.CUSTOM.contains(statResourceLocation)
|
||||
if (!exists) return@put DoubleValue.ZERO
|
||||
|
||||
val stat = net.minecraft.stats.Stats.CUSTOM.get(statResourceLocation)
|
||||
val value = player.stats.getValue(stat)
|
||||
|
||||
DoubleValue(value)
|
||||
}
|
||||
map["get_starter_uuid"] = { _ ->
|
||||
Cobblemon.playerDataManager.getGenericData(player).starterUUID?.let {
|
||||
StringValue(
|
||||
it.toString()
|
||||
)
|
||||
} ?: DoubleValue.ZERO
|
||||
}
|
||||
map["has_chosen_starter"] = { _ -> DoubleValue(Cobblemon.playerDataManager.getGenericData(player).starterSelected) }
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+71
-37
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.pokedex.AbstractPokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtCount
|
||||
import com.cobblemon.mod.common.api.pokedex.CaughtPercent
|
||||
import com.cobblemon.mod.common.api.pokedex.Dexes
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexManager
|
||||
import com.cobblemon.mod.common.api.pokedex.SeenCount
|
||||
@@ -27,57 +28,90 @@ import com.cobblemon.mod.common.util.getStringOrNull
|
||||
object PokedexMoLangFunctions : AbstractMoLangFunctionHolder<AbstractPokedexManager>() {
|
||||
override fun AbstractPokedexManager.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pokedex = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["get_species_record"] = { params ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["get_species_record"] = { params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
pokedex.speciesRecords[speciesId]?.struct ?: QueryStruct(hashMapOf())
|
||||
}
|
||||
|
||||
map["has_seen"] = put@{ params ->
|
||||
functions["has_seen"] = hasSeen@{ params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
val formName =
|
||||
params.getStringOrNull(1)
|
||||
?: return@hasSeen DoubleValue(
|
||||
pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.SEEN.ordinal,
|
||||
)
|
||||
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId).ordinal >= PokedexEntryProgress.SEEN.ordinal)
|
||||
} else {
|
||||
return@put DoubleValue(
|
||||
(pokedex.getSpeciesRecord(speciesId)?.getFormRecord(formName)?.knowledge?.ordinal
|
||||
?: 0) >= PokedexEntryProgress.SEEN.ordinal
|
||||
)
|
||||
}
|
||||
DoubleValue(
|
||||
(
|
||||
pokedex
|
||||
.getSpeciesRecord(speciesId)
|
||||
?.getFormRecord(formName)
|
||||
?.knowledge
|
||||
?.ordinal
|
||||
?: 0
|
||||
) >= PokedexEntryProgress.SEEN.ordinal,
|
||||
)
|
||||
}
|
||||
|
||||
map["has_caught"] = put@{ params ->
|
||||
functions["has_caught"] = hasCaught@{ params ->
|
||||
val speciesId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val formName = params.getStringOrNull(1)
|
||||
if (formName == null) {
|
||||
return@put DoubleValue(pokedex.getHighestKnowledgeForSpecies(speciesId) == PokedexEntryProgress.OWNED)
|
||||
} else {
|
||||
return@put DoubleValue(
|
||||
pokedex.getSpeciesRecord(speciesId)
|
||||
?.getFormRecord(formName)?.knowledge == PokedexEntryProgress.OWNED
|
||||
val formName =
|
||||
params.getStringOrNull(1) ?: return@hasCaught DoubleValue(
|
||||
pokedex.getHighestKnowledgeForSpecies(
|
||||
speciesId,
|
||||
) == PokedexEntryProgress.OWNED,
|
||||
)
|
||||
}
|
||||
DoubleValue(
|
||||
pokedex
|
||||
.getSpeciesRecord(speciesId)
|
||||
?.getFormRecord(formName)
|
||||
?.knowledge == PokedexEntryProgress.OWNED,
|
||||
)
|
||||
}
|
||||
map["caught_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
|
||||
map["seen_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
|
||||
map["caught_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
|
||||
map["seen_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
|
||||
functions["caught_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtCount)) }
|
||||
functions["seen_count"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenCount)) }
|
||||
functions["caught_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(CaughtPercent)) }
|
||||
functions["seen_percent"] = { DoubleValue(pokedex.getGlobalCalculatedValue(SeenPercent)) }
|
||||
|
||||
if (pokedex is PokedexManager) {
|
||||
map["player_id"] = { StringValue(pokedex.uuid.toString()) }
|
||||
map["see"] = put@{ params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.encounter(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map["catch"] = put@{ params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.obtain(pokemon)
|
||||
return@put DoubleValue.ONE
|
||||
functions["dex_caught_count"] = { params ->
|
||||
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, CaughtCount))
|
||||
else DoubleValue.ZERO
|
||||
}
|
||||
functions["dex_seen_count"] = { params ->
|
||||
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, SeenCount))
|
||||
else DoubleValue.ZERO
|
||||
}
|
||||
functions["dex_caught_percent"] = { params ->
|
||||
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, CaughtPercent).toDouble())
|
||||
else DoubleValue.ZERO
|
||||
}
|
||||
functions["dex_seen_percent"] = { params ->
|
||||
val dexId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
if (Dexes.dexEntryMap.containsKey(dexId)) DoubleValue(pokedex.getDexCalculatedValue(dexId, SeenPercent).toDouble())
|
||||
else DoubleValue.ZERO
|
||||
}
|
||||
|
||||
when (pokedex) {
|
||||
is PokedexManager -> {
|
||||
functions["player_id"] = { StringValue(pokedex.uuid.toString()) }
|
||||
functions["see"] = { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.encounter(pokemon)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
functions["catch"] = { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0).obj
|
||||
pokedex.obtain(pokemon)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+55
-40
@@ -32,100 +32,113 @@ import com.cobblemon.mod.common.util.withQueryValue
|
||||
object PokemonEntityMoLangFunctions : AbstractMoLangFunctionHolder<PokemonEntity>() {
|
||||
override fun PokemonEntity.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pokemonEntity = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["is_busy"] = { DoubleValue(pokemonEntity.isBusy) }
|
||||
map["in_battle"] = { DoubleValue(pokemonEntity.isBattling) }
|
||||
map["is_moving"] = { DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
map["is_flying"] = { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
map["get_riding_state"] = { params ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["is_busy"] = { DoubleValue(pokemonEntity.isBusy) }
|
||||
functions["in_battle"] = { DoubleValue(pokemonEntity.isBattling) }
|
||||
functions["is_moving"] =
|
||||
{ DoubleValue((pokemonEntity.moveControl as? PokemonMoveControl)?.hasWanted() == true) }
|
||||
functions["is_flying"] = { DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.FLYING)) }
|
||||
functions["get_riding_state"] = { params ->
|
||||
val name = params.getStringOrNull(0)
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions[name]?.apply(MoParams.EMPTY) as? MoValue ?: moValue
|
||||
}
|
||||
}
|
||||
map["is_gliding"] = {
|
||||
functions["is_gliding"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["gliding"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_sprinting"] = {
|
||||
functions["is_sprinting"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["sprinting"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_drifting"] = {
|
||||
functions["is_drifting"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["drifting"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_powered_drifting"] = {
|
||||
functions["is_powered_drifting"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["powered_drifting"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["in_air"] = {
|
||||
functions["in_air"] = {
|
||||
pokemonEntity.ifRidingAvailableSupply(DoubleValue.ZERO) { behaviour, settings, state ->
|
||||
val moValue = behaviour.asMoLangValue(settings, state, pokemonEntity)
|
||||
return@ifRidingAvailableSupply moValue.functions["in_air"]?.apply(MoParams.EMPTY) as? MoValue
|
||||
?: DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["is_wild"] = { DoubleValue(pokemonEntity.ownerUUID == null) }
|
||||
map["is_in_party"] = { DoubleValue(pokemonEntity.pokemon.storeCoordinates.get()?.store is PartyStore) }
|
||||
map["is_ridden"] = { DoubleValue(pokemonEntity.hasControllingPassenger()) }
|
||||
map["has_aspect"] = { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
map["is_pokemon"] = { DoubleValue.ONE }
|
||||
map["is_holding_item"] = {
|
||||
DoubleValue(!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
|
||||
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
})
|
||||
functions["is_wild"] = { DoubleValue(pokemonEntity.ownerUUID == null) }
|
||||
functions["is_in_party"] = {
|
||||
DoubleValue(
|
||||
pokemonEntity.pokemon.storeCoordinates
|
||||
.get()
|
||||
?.store is PartyStore,
|
||||
)
|
||||
}
|
||||
map["riding_style"] = {
|
||||
StringValue(pokemonEntity.ifRidingAvailableSupply("") { behaviour, settings, state ->
|
||||
behaviour.getRidingStyle(settings, state).name
|
||||
})
|
||||
functions["is_ridden"] = { DoubleValue(pokemonEntity.hasControllingPassenger()) }
|
||||
functions["has_aspect"] = { DoubleValue(it.getString(0) in pokemonEntity.aspects) }
|
||||
functions["is_pokemon"] = { DoubleValue.ONE }
|
||||
functions["is_holding_item"] = {
|
||||
DoubleValue(
|
||||
!pokemonEntity.entityData.get(PokemonEntity.SHOWN_HELD_ITEM).let {
|
||||
it.isEmpty || it.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS) || it.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
},
|
||||
)
|
||||
}
|
||||
map["is_wearing_hat"] = {
|
||||
functions["riding_style"] = {
|
||||
StringValue(
|
||||
pokemonEntity.ifRidingAvailableSupply("") { behaviour, settings, state ->
|
||||
behaviour.getRidingStyle(settings, state).name
|
||||
},
|
||||
)
|
||||
}
|
||||
functions["is_wearing_hat"] = {
|
||||
DoubleValue(
|
||||
pokemonEntity.entityData
|
||||
.get(PokemonEntity.SHOWN_HELD_ITEM)
|
||||
.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS)
|
||||
.`is`(CobblemonItemTags.WEARABLE_HAT_ITEMS),
|
||||
)
|
||||
}
|
||||
map["is_wearing_face"] = {
|
||||
functions["is_wearing_face"] = {
|
||||
DoubleValue(
|
||||
pokemonEntity.entityData
|
||||
.get(PokemonEntity.SHOWN_HELD_ITEM)
|
||||
.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS)
|
||||
.`is`(CobblemonItemTags.WEARABLE_FACE_ITEMS),
|
||||
)
|
||||
}
|
||||
map["is_pastured"] = {
|
||||
functions["is_pastured"] = {
|
||||
DoubleValue((pokemonEntity.tethering != null))
|
||||
}
|
||||
map["pasture_conflict_enabled"] = {
|
||||
functions["pasture_conflict_enabled"] = {
|
||||
DoubleValue(pokemonEntity.getBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT))
|
||||
}
|
||||
map["run_action_effect"] = runActionEffect@{ params ->
|
||||
functions["run_action_effect"] = runActionEffect@{ params ->
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.environment.cloneFrom(params.environment)
|
||||
runtime.withQueryValue("entity", pokemonEntity.struct)
|
||||
val actionEffect = ActionEffects.actionEffects[params.getString(0).asIdentifierDefaultingNamespace()]
|
||||
if (actionEffect != null) {
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
providers = mutableListOf(TargetsProvider(pokemonEntity)),
|
||||
runtime = runtime,
|
||||
level = pokemonEntity.level()
|
||||
)
|
||||
val context =
|
||||
ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
providers = mutableListOf(TargetsProvider(pokemonEntity)),
|
||||
runtime = runtime,
|
||||
level = pokemonEntity.level(),
|
||||
)
|
||||
// This was taken from NPC run_action_effect but this variable isn't used, so I just commented it out.
|
||||
// pokemonEntity.actionEffect = context
|
||||
pokemonEntity.brain.setMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT, context)
|
||||
@@ -135,14 +148,16 @@ object PokemonEntityMoLangFunctions : AbstractMoLangFunctionHolder<PokemonEntity
|
||||
pokemonEntity.brain.getMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT).orElse(null)
|
||||
if (pokemonActionEffect == context && pokemonEntity.brain.isActive(CobblemonActivities.ACTION_EFFECT)) {
|
||||
pokemonEntity.brain.eraseMemory(CobblemonMemories.ACTIVE_ACTION_EFFECT)
|
||||
//pokemonEntity.actionEffect = null
|
||||
// pokemonEntity.actionEffect = null
|
||||
}
|
||||
}
|
||||
|
||||
return@runActionEffect DoubleValue.ONE
|
||||
}
|
||||
return@runActionEffect DoubleValue.ZERO
|
||||
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+128
-127
@@ -33,218 +33,214 @@ import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import com.cobblemon.mod.common.util.getIntOrNull
|
||||
import com.cobblemon.mod.common.util.server
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
import kotlin.random.Random
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import kotlin.random.Random
|
||||
|
||||
object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
override fun Pokemon.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val pokemon = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["id"] = { StringValue(pokemon.uuid.toString()) }
|
||||
map["nickname"] = { StringValue(pokemon.nickname.toString()) }
|
||||
map["level"] = { DoubleValue(pokemon.level.toDouble()) }
|
||||
map["max_hp"] = { DoubleValue(pokemon.maxHealth.toDouble()) }
|
||||
map["current_hp"] = { DoubleValue(pokemon.currentHealth.toDouble()) }
|
||||
map["friendship"] = { DoubleValue(pokemon.friendship.toDouble()) }
|
||||
map["max_fullness"] = { DoubleValue(pokemon.getMaxFullness().toDouble()) }
|
||||
map["fullness"] = { DoubleValue(pokemon.currentFullness.toDouble()) }
|
||||
map["lose_fullness"] = { params ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["id"] = { StringValue(pokemon.uuid.toString()) }
|
||||
functions["nickname"] = { StringValue(pokemon.nickname.toString()) }
|
||||
functions["level"] = { DoubleValue(pokemon.level.toDouble()) }
|
||||
functions["max_hp"] = { DoubleValue(pokemon.maxHealth.toDouble()) }
|
||||
functions["current_hp"] = { DoubleValue(pokemon.currentHealth.toDouble()) }
|
||||
functions["friendship"] = { DoubleValue(pokemon.friendship.toDouble()) }
|
||||
functions["max_fullness"] = { DoubleValue(pokemon.getMaxFullness().toDouble()) }
|
||||
functions["fullness"] = { DoubleValue(pokemon.currentFullness.toDouble()) }
|
||||
functions["lose_fullness"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
pokemon.loseFullness(amount.toInt())
|
||||
}
|
||||
map["feed_pokemon"] = { params ->
|
||||
functions["feed_pokemon"] = { params ->
|
||||
val amount = params.getDouble(0)
|
||||
val playSound = params.getBooleanOrNull(1) ?: true
|
||||
pokemon.feedPokemon(amount.toInt(), playSound)
|
||||
}
|
||||
map["behaviour"] = { pokemon.form.behaviour.struct }
|
||||
map["behavior"] = { pokemon.form.behaviour.struct } // Inferior
|
||||
map["pokeball"] = { StringValue(pokemon.caughtBall.toString()) }
|
||||
map["ability"] = { StringValue(pokemon.ability.name) }
|
||||
map["has_learned"] = put@{ params ->
|
||||
functions["behaviour"] = { pokemon.form.behaviour.struct }
|
||||
functions["behavior"] = { pokemon.form.behaviour.struct } // Inferior
|
||||
functions["pokeball"] = { StringValue(pokemon.caughtBall.toString()) }
|
||||
functions["ability"] = { StringValue(pokemon.ability.name) }
|
||||
functions["has_learned"] = hasLearned@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val move = pokemon.allAccessibleMoves.find { it.name == moveName }
|
||||
if (move != null) {
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
if (move != null) return@hasLearned DoubleValue.ONE
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["moveset"] = {
|
||||
functions["moveset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.moveSet.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["evs"] = {
|
||||
functions["evs"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.evs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["ivs"] = {
|
||||
functions["ivs"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.getOrDefault(stat).toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["hyper_trained_ivs"] = {
|
||||
functions["hyper_trained_ivs"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.ivs.hyperTrainedIVs[stat] ?: -1.0) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["ride_boosts"] = {
|
||||
functions["ride_boosts"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in RidingStat.entries) {
|
||||
struct.addFunction(stat.name.lowercase()) { DoubleValue(pokemon.getRideBoost(stat)) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["natdex_number"] = {
|
||||
functions["natdex_number"] = {
|
||||
DoubleValue(pokemon.species.nationalPokedexNumber.toDouble())
|
||||
}
|
||||
map["types"] = {
|
||||
pokemon.form.types.map { it.showdownId }.asArrayValue(::StringValue)
|
||||
functions["types"] = {
|
||||
pokemon.form.types
|
||||
.map { it.showdownId }
|
||||
.asArrayValue(::StringValue)
|
||||
}
|
||||
map["gender_ratio"] = {
|
||||
functions["gender_ratio"] = {
|
||||
DoubleValue(pokemon.form.maleRatio.toDouble())
|
||||
}
|
||||
map["ev_yield"] = {
|
||||
functions["ev_yield"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.form.evYield[stat]?.toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["base_stats"] = {
|
||||
functions["base_stats"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for (stat in Stats.PERMANENT) {
|
||||
struct.addFunction(stat.showdownId) { DoubleValue(pokemon.form.baseStats[stat]?.toDouble()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["catch_rate"] = {
|
||||
functions["catch_rate"] = {
|
||||
DoubleValue(pokemon.form.catchRate.toDouble())
|
||||
}
|
||||
map["base_experience_yield"] = {
|
||||
functions["base_experience_yield"] = {
|
||||
DoubleValue(pokemon.form.baseExperienceYield.toDouble())
|
||||
}
|
||||
map["drops"] = {
|
||||
functions["drops"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, drop) in pokemon.form.drops.entries.withIndex()) {
|
||||
for ((index, drop) in pokemon.form.drops.entries
|
||||
.withIndex()) {
|
||||
struct.addFunction(index.toString()) { drop }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["tm_learnset"] = {
|
||||
functions["tm_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.tmMoves.withIndex()) {
|
||||
for ((index, move) in pokemon.form.moves.tmMoves
|
||||
.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["egg_learnset"] = {
|
||||
functions["egg_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.eggMoves.withIndex()) {
|
||||
for ((index, move) in pokemon.form.moves.eggMoves
|
||||
.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["tutor_learnset"] = {
|
||||
functions["tutor_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.tutorMoves.withIndex()) {
|
||||
for ((index, move) in pokemon.form.moves.tutorMoves
|
||||
.withIndex()) {
|
||||
struct.addFunction(index.toString()) { move.struct }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["level_learnset"] = {
|
||||
functions["level_learnset"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, move) in pokemon.form.moves.levelUpMoves) {
|
||||
struct.addFunction(index.toString()) { move }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["ability_pool"] = {
|
||||
functions["ability_pool"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, ability) in pokemon.form.abilities.withIndex()) {
|
||||
struct.addFunction(index.toString()) { StringValue(ability.toString()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["egg_groups"] = {
|
||||
functions["egg_groups"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, group) in pokemon.form.eggGroups.withIndex()) {
|
||||
struct.addFunction(index.toString()) { StringValue(group.toString()) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["egg_cycles"] = {
|
||||
functions["egg_cycles"] = {
|
||||
DoubleValue(pokemon.species.eggCycles.toDouble())
|
||||
}
|
||||
map["labels"] = {
|
||||
functions["labels"] = {
|
||||
val struct = QueryStruct(hashMapOf())
|
||||
for ((index, label) in pokemon.form.labels.withIndex()) {
|
||||
struct.addFunction(index.toString()) { StringValue(label) }
|
||||
}
|
||||
struct
|
||||
}
|
||||
map["aspects"] = {
|
||||
functions["aspects"] = {
|
||||
val aspects = pokemon.aspects
|
||||
aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map["form_aspects"] = {
|
||||
functions["form_aspects"] = {
|
||||
val aspects = pokemon.form.aspects
|
||||
aspects.asArrayValue { StringValue(it) }
|
||||
}
|
||||
map["form_name"] = {
|
||||
functions["form_name"] = {
|
||||
StringValue(pokemon.form.name)
|
||||
}
|
||||
// Yes, this is the lazy call for a single hardcoded pre-evolution.
|
||||
// Lol. Lmao, even.
|
||||
// TO-DO: Subscribe to [PokemonSpecies.observable].
|
||||
// Gito: surely this is just a typo, right? not touching the function name just in case
|
||||
map["prevolution"] = preEvolution@{
|
||||
val preEvolution = pokemon.species.preEvolution ?: return@preEvolution DoubleValue.ZERO
|
||||
return@preEvolution preEvolution
|
||||
}
|
||||
map["nature"] = { StringValue(pokemon.nature.toString()) }
|
||||
map["is_wild"] = { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
map["is_shiny"] = { DoubleValue(pokemon.shiny) }
|
||||
map["is_in_party"] = { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
|
||||
map["species"] = { pokemon.species.struct }
|
||||
map["form"] = { StringValue(pokemon.form.name) }
|
||||
map["weight"] = { DoubleValue(pokemon.species.weight.toDouble()) }
|
||||
map["matches"] = { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
|
||||
map["apply"] = { params ->
|
||||
functions["pre_evolution"] = { pokemon.species.preEvolution ?: DoubleValue.ZERO }
|
||||
functions["nature"] = { StringValue(pokemon.nature.toString()) }
|
||||
functions["is_wild"] = { DoubleValue(pokemon.entity?.let { it.ownerUUID == null } == true) }
|
||||
functions["is_shiny"] = { DoubleValue(pokemon.shiny) }
|
||||
functions["is_in_party"] = { DoubleValue(pokemon.storeCoordinates.get()?.store is PartyStore) }
|
||||
functions["species"] = { pokemon.species.struct }
|
||||
functions["form"] = { StringValue(pokemon.form.name) }
|
||||
functions["weight"] = { DoubleValue(pokemon.species.weight.toDouble()) }
|
||||
functions["matches"] = { params -> DoubleValue(params.getString(0).toProperties().matches(pokemon)) }
|
||||
functions["apply"] = { params ->
|
||||
params.getString(0).toProperties().apply(pokemon)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["owner"] = { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["held_item"] = { pokemon.heldItem().asMoLangValue(server()!!.registryAccess()) }
|
||||
map["remove_held_item"] = { _ ->
|
||||
pokemon.removeHeldItem()
|
||||
}
|
||||
map["add_aspects"] = { params ->
|
||||
functions["owner"] = { pokemon.getOwnerPlayer()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
functions["held_item"] = { pokemon.heldItem().asMoLangValue(server()!!.registryAccess()) }
|
||||
functions["remove_held_item"] = { pokemon.removeHeldItem() }
|
||||
functions["add_aspects"] = { params ->
|
||||
for (aspect in params.params) pokemon.forcedAspects += aspect.asString()
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
map["remove_aspects"] = { params ->
|
||||
functions["remove_aspects"] = { params ->
|
||||
for (aspect in params.params) pokemon.forcedAspects -= aspect.asString()
|
||||
pokemon.updateAspects()
|
||||
}
|
||||
map["cosmetic_item"] = {
|
||||
functions["cosmetic_item"] = {
|
||||
pokemon.cosmeticItem().asMoLangValue(server()!!.registryAccess())
|
||||
}
|
||||
map["remove_cosmetic_item"] = { _ ->
|
||||
pokemon.removeCosmeticItem()
|
||||
}
|
||||
map["marks"] = { _ -> pokemon.marks.asArrayValue { StringValue(it.identifier.toString()) } }
|
||||
map["has_mark"] = { params ->
|
||||
functions["remove_cosmetic_item"] = { pokemon.removeCosmeticItem() }
|
||||
functions["marks"] = { pokemon.marks.asArrayValue { StringValue(it.identifier.toString()) } }
|
||||
functions["has_mark"] = { params ->
|
||||
var hasMark = false
|
||||
val identifier = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
@@ -254,7 +250,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
|
||||
DoubleValue(hasMark)
|
||||
}
|
||||
map["remove_marks"] = { params ->
|
||||
functions["remove_marks"] = { params ->
|
||||
var removedMark = false
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
@@ -267,7 +263,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
|
||||
DoubleValue(removedMark)
|
||||
}
|
||||
map["add_marks"] = { params ->
|
||||
functions["add_marks"] = { params ->
|
||||
var appliedMark = false
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
@@ -280,7 +276,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
|
||||
DoubleValue(appliedMark)
|
||||
}
|
||||
map["add_marks_with_chance"] = { params ->
|
||||
functions["add_marks_with_chance"] = { params ->
|
||||
var appliedMark = false
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
@@ -298,17 +294,15 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
|
||||
DoubleValue(appliedMark)
|
||||
}
|
||||
map["add_potential_marks"] = { params ->
|
||||
functions["add_potential_marks"] = { params ->
|
||||
for (param in params.params) {
|
||||
val identifier = param.asString().asIdentifierDefaultingNamespace()
|
||||
val mark = Marks.getByIdentifier(identifier)
|
||||
if (mark != null) pokemon.addPotentialMark(mark)
|
||||
}
|
||||
}
|
||||
map["apply_potential_marks"] = put@{
|
||||
return@put DoubleValue(pokemon.applyPotentialMarks())
|
||||
}
|
||||
map["hyper_train_iv"] = hyperTrainIv@{ params ->
|
||||
functions["apply_potential_marks"] = { DoubleValue(pokemon.applyPotentialMarks()) }
|
||||
functions["hyper_train_iv"] = hyperTrainIv@{ params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = params.getIntOrNull(1) ?: IVs.MAX_VALUE
|
||||
@@ -321,12 +315,12 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
return@hyperTrainIv DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["add_exp"] = { params ->
|
||||
functions["add_exp"] = { params ->
|
||||
val exp = params.getDouble(0).toInt()
|
||||
pokemon.addExperience(SidemodExperienceSource("molang"), exp)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["set_iv"] = setIv@{ params ->
|
||||
functions["set_iv"] = setIv@{ params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = params.getIntOrNull(1)?.coerceIn(0, IVs.MAX_VALUE) ?: IVs.MAX_VALUE
|
||||
@@ -338,7 +332,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
return@setIv DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_ev"] = setEv@{ params ->
|
||||
functions["set_ev"] = setEv@{ params ->
|
||||
val statId = params.getString(0)
|
||||
val stat = Stats.getStat(statId)
|
||||
val value = (params.getIntOrNull(1) ?: 0).coerceIn(0, 255)
|
||||
@@ -350,49 +344,54 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
return@setEv DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map["set_ride_boost"] = setRideBoost@{ params ->
|
||||
functions["set_ride_boost"] = setRideBoost@{ params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
|
||||
?: return@setRideBoost DoubleValue.ZERO
|
||||
val stat =
|
||||
RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
|
||||
?: return@setRideBoost DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@setRideBoost DoubleValue.ZERO
|
||||
|
||||
pokemon.setRideBoost(stat, value)
|
||||
return@setRideBoost DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["add_ride_boost"] = addRideBoost@{ params ->
|
||||
functions["add_ride_boost"] = addRideBoost@{ params ->
|
||||
val statName = params.getString(0).uppercase()
|
||||
val stat = RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
|
||||
?: return@addRideBoost DoubleValue.ZERO
|
||||
val stat =
|
||||
RidingStat.entries.find { it.name.equals(statName, ignoreCase = true) }
|
||||
?: return@addRideBoost DoubleValue.ZERO
|
||||
val value = params.getDoubleOrNull(1)?.toFloat() ?: return@addRideBoost DoubleValue.ZERO
|
||||
|
||||
return@addRideBoost DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
|
||||
DoubleValue(if (pokemon.addRideBoost(stat, value)) 1.0 else 0.0)
|
||||
}
|
||||
map["initialize_moveset"] = put@{ params ->
|
||||
functions["initialize_moveset"] = initializeMoveset@{ params ->
|
||||
val param = params.get<MoValue>(0) ?: StringValue(pokemon.form.defaultWildMovesetBuilder.toString())
|
||||
if (param is DoubleValue) {
|
||||
pokemon.initializeMoveset(param == DoubleValue.ONE)
|
||||
} else {
|
||||
val movesetBuilderId = ResourceLocation.parse(param.asString())
|
||||
val movesetBuilder = CobblemonMovesetBuilders.movesetBuilders[movesetBuilderId]
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("Tried initializing moveset from moveset builder ${param.asString()} but it does not exist.")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
val movesetBuilder =
|
||||
CobblemonMovesetBuilders.movesetBuilders[movesetBuilderId]
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error(
|
||||
"Tried initializing moveset from moveset builder ${param.asString()} but it does not exist.",
|
||||
)
|
||||
return@initializeMoveset DoubleValue.ZERO
|
||||
}
|
||||
pokemon.initializeMovesetFrom(movesetBuilder)
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["validate_moveset"] = { params ->
|
||||
functions["validate_moveset"] = { params ->
|
||||
val includeLegacy = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.validateMoveset(includeLegacy)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["teach_learnable_moves"] = { params ->
|
||||
functions["teach_learnable_moves"] = { params ->
|
||||
val includeLegacy = params.getBooleanOrNull(0) ?: true
|
||||
pokemon.teachLearnableMoves(includeLegacy)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["teach_move"] = teachMove@{ params ->
|
||||
functions["teach_move"] = teachMove@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@teachMove DoubleValue.ZERO
|
||||
val bypass = params.getBooleanOrNull(1) ?: false
|
||||
@@ -402,8 +401,9 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
return@teachMove DoubleValue.ZERO
|
||||
}
|
||||
|
||||
val alreadyKnows = pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
|
||||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
|
||||
val alreadyKnows =
|
||||
pokemon.moveSet.getMoves().any { it.template == moveTemplate } ||
|
||||
pokemon.benchedMoves.any { it.moveTemplate == moveTemplate }
|
||||
if (alreadyKnows) {
|
||||
return@teachMove DoubleValue.ZERO
|
||||
}
|
||||
@@ -416,42 +416,43 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
|
||||
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["can_learn_move"] = canLearnMove@{ params ->
|
||||
functions["can_learn_move"] = canLearnMove@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName) ?: return@canLearnMove DoubleValue.ZERO
|
||||
val includeLegacy = params.getBooleanOrNull(1) ?: true
|
||||
|
||||
val canLearn = if (includeLegacy) {
|
||||
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
} else {
|
||||
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
|
||||
}
|
||||
return@canLearnMove DoubleValue(if (canLearn) 1.0 else 0.0)
|
||||
val canLearn =
|
||||
if (includeLegacy) {
|
||||
LearnsetQuery.ANY.canLearn(moveTemplate, pokemon.form.moves)
|
||||
} else {
|
||||
LearnsetQuery.LEGAL.canLearn(moveTemplate, pokemon.form.moves)
|
||||
}
|
||||
|
||||
DoubleValue(if (canLearn) 1.0 else 0.0)
|
||||
}
|
||||
map["unlearn_move"] = unlearnMove@{ params ->
|
||||
functions["unlearn_move"] = unlearnMove@{ params ->
|
||||
val moveName = params.getString(0)
|
||||
val moveTemplate = Moves.getByName(moveName)
|
||||
if (moveTemplate != null) {
|
||||
pokemon.unlearnMove(moveTemplate)
|
||||
return@unlearnMove DoubleValue.ONE
|
||||
} else {
|
||||
return@unlearnMove DoubleValue.ZERO
|
||||
}
|
||||
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["can_evolve"] = { _ ->
|
||||
DoubleValue(pokemon.evolutions.any())
|
||||
}
|
||||
map["force_evolve"] = forceEvolve@{ params ->
|
||||
functions["can_evolve"] = { DoubleValue(pokemon.evolutions.any()) }
|
||||
functions["force_evolve"] = forceEvolve@{ params ->
|
||||
val idx = params.getInt(0)
|
||||
val evolution = if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@forceEvolve DoubleValue.ZERO
|
||||
val evolution =
|
||||
if (idx >= 0) pokemon.evolutions.elementAtOrNull(idx) else return@forceEvolve DoubleValue.ZERO
|
||||
evolution?.forceEvolve(pokemon)
|
||||
StringValue(evolution.toString())
|
||||
}
|
||||
|
||||
map["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
|
||||
map["size_category"] = { _ -> StringValue(pokemon.getSizeCategory().name) }
|
||||
functions["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
functions["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
|
||||
functions["size_category"] = { StringValue(pokemon.getSizeCategory().name) }
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+20
-22
@@ -20,52 +20,50 @@ import com.cobblemon.mod.common.util.getStringOrNull
|
||||
object PokemonPropertiesMoLangFunctions : AbstractMoLangFunctionHolder<PokemonProperties>() {
|
||||
override fun PokemonProperties.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val props = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["level"] = { DoubleValue(props.level?.toDouble() ?: 0) }
|
||||
map["set_level"] = { params ->
|
||||
functions["level"] = { DoubleValue(props.level?.toDouble() ?: 0) }
|
||||
functions["set_level"] = { params ->
|
||||
props.level = params.getIntOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["shiny"] = { DoubleValue(props.shiny) }
|
||||
map["set_shiny"] = { params ->
|
||||
functions["shiny"] = { DoubleValue(props.shiny) }
|
||||
functions["set_shiny"] = { params ->
|
||||
props.shiny = params.getBooleanOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["species"] = { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
|
||||
map["set_species"] = { params ->
|
||||
functions["species"] = { props.species?.let { StringValue(it) } ?: DoubleValue.ZERO }
|
||||
functions["set_species"] = { params ->
|
||||
props.species = params.getStringOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["gender"] = { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
|
||||
map["set_gender"] = { params ->
|
||||
functions["gender"] = { props.gender?.let { StringValue(it.name) } ?: DoubleValue.ZERO }
|
||||
functions["set_gender"] = { params ->
|
||||
props.gender = params.getStringOrNull(0)?.let { Gender.valueOf(it) }
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["form"] = { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
|
||||
map["ivs"] = ivs@{
|
||||
functions["form"] = { props.form?.let { StringValue(it) } ?: DoubleValue.ZERO }
|
||||
functions["ivs"] = ivs@{
|
||||
val ivs = props.ivs ?: return@ivs DoubleValue.ZERO
|
||||
|
||||
return@ivs ivs.struct
|
||||
ivs.struct
|
||||
}
|
||||
map["evs"] = evs@{
|
||||
functions["evs"] = evs@{
|
||||
val evs = props.evs ?: return@evs DoubleValue.ZERO
|
||||
|
||||
return@evs evs.struct
|
||||
evs.struct
|
||||
}
|
||||
map["friendship"] = { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
|
||||
map["set_friendship"] = { params ->
|
||||
functions["friendship"] = { DoubleValue(props.friendship?.toDouble() ?: DoubleValue.ZERO) }
|
||||
functions["set_friendship"] = { params ->
|
||||
props.friendship = params.getIntOrNull(0)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["create"] = {
|
||||
val pokemon = props.create()
|
||||
pokemon.struct
|
||||
}
|
||||
map["to_string"] = { params ->
|
||||
functions["create"] = { (props.create()).struct }
|
||||
functions["to_string"] = { params ->
|
||||
val separator = params.getStringOrNull(0) ?: " "
|
||||
StringValue(props.asString(separator = separator))
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+28
-27
@@ -22,83 +22,84 @@ import com.cobblemon.mod.common.util.asUUID
|
||||
import com.cobblemon.mod.common.util.getPlayer
|
||||
import com.cobblemon.mod.common.util.toProperties
|
||||
|
||||
object PokemonStoreMoLangFunctions: AbstractMoLangFunctionHolder<PokemonStore<*>>() {
|
||||
object PokemonStoreMoLangFunctions : AbstractMoLangFunctionHolder<PokemonStore<*>>() {
|
||||
override fun PokemonStore<*>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val store = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
map["uuid"] = { StringValue(store.uuid.toString()) }
|
||||
map["add"] = { params ->
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["uuid"] = { StringValue(store.uuid.toString()) }
|
||||
functions["add"] = { params ->
|
||||
val pokemon = params.get<ObjectValue<Pokemon>>(0)
|
||||
DoubleValue(store.add(pokemon.obj))
|
||||
}
|
||||
map["add_by_properties"] = { params ->
|
||||
functions["add_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val player = (store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store
|
||||
val player =
|
||||
(store as? PlayerPartyStore)?.playerUUID?.getPlayer() // Only really know for sure when it's a player party store
|
||||
val pokemon = props.create(player = player)
|
||||
DoubleValue(store.add(pokemon))
|
||||
}
|
||||
map["find_by_properties"] = { params ->
|
||||
functions["find_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.find { props.matches(it) }
|
||||
pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map["find_all_by_properties"] = { params ->
|
||||
functions["find_all_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
val pokemon = store.filter { props.matches(it) }
|
||||
ArrayStruct(pokemon.mapIndexed { index, value -> "$index" to value.asStruct() }.toMap())
|
||||
}
|
||||
map["find_by_id"] = { params ->
|
||||
functions["find_by_id"] = { params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id }
|
||||
pokemon?.asStruct() ?: DoubleValue.ZERO
|
||||
}
|
||||
map["remove_by_id"] = removeById@{ params ->
|
||||
functions["remove_by_id"] = removeById@{ params ->
|
||||
val id = params.getString(0).asUUID
|
||||
val pokemon = store.find { it.uuid == id } ?: return@removeById DoubleValue.ZERO
|
||||
return@removeById DoubleValue(store.remove(pokemon))
|
||||
DoubleValue(store.remove(pokemon))
|
||||
}
|
||||
map["average_level"] = averageLevel@{ _ ->
|
||||
functions["average_level"] = averageLevel@{
|
||||
var numberOfPokemon = 0
|
||||
var totalLevel = 0
|
||||
for (pokemon in store) {
|
||||
totalLevel += pokemon.level
|
||||
numberOfPokemon++
|
||||
}
|
||||
if (numberOfPokemon == 0) {
|
||||
return@averageLevel DoubleValue.ZERO
|
||||
}
|
||||
return@averageLevel DoubleValue(totalLevel.toDouble() / numberOfPokemon)
|
||||
|
||||
if (numberOfPokemon == 0) return@averageLevel DoubleValue.ZERO
|
||||
|
||||
DoubleValue(totalLevel.toDouble() / numberOfPokemon)
|
||||
}
|
||||
map["count"] = { _ -> DoubleValue(store.count()) }
|
||||
map["count_by_properties"] = { params ->
|
||||
functions["count"] = { DoubleValue(store.count()) }
|
||||
functions["count_by_properties"] = { params ->
|
||||
val props = params.getString(0).toProperties()
|
||||
DoubleValue(store.count { props.matches(it) })
|
||||
}
|
||||
map["highest_level"] = {
|
||||
functions["highest_level"] = {
|
||||
val highest = store.maxOfOrNull { it.level } ?: 0
|
||||
DoubleValue(highest)
|
||||
}
|
||||
map["lowest_level"] = {
|
||||
functions["lowest_level"] = {
|
||||
val lowest = store.minOfOrNull { it.level } ?: 0
|
||||
DoubleValue(lowest)
|
||||
}
|
||||
map["heal"] = {
|
||||
functions["heal"] = {
|
||||
for (pokemon in store) {
|
||||
pokemon.heal()
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map["healing_remainder_percent"] = { _ ->
|
||||
functions["healing_remainder_percent"] = {
|
||||
var totalPercent = 0.0f
|
||||
for (pokemon in store) {
|
||||
totalPercent += (1.0f - (pokemon.currentHealth.toFloat() / pokemon.maxHealth))
|
||||
}
|
||||
DoubleValue(totalPercent)
|
||||
}
|
||||
map["has_usable_pokemon"] = { _ -> DoubleValue(store.any { !it.isFainted() }) }
|
||||
map["pokemon"] = {
|
||||
store.map { it.asStruct() }.asArrayValue()
|
||||
}
|
||||
return map
|
||||
functions["has_usable_pokemon"] = { DoubleValue(store.any { !it.isFainted() }) }
|
||||
functions["pokemon"] = { store.map { it.asStruct() }.asArrayValue() }
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+21
-20
@@ -25,62 +25,63 @@ import java.util.*
|
||||
object ServerMoLangFunctions : AbstractMoLangFunctionHolder<MinecraftServer>() {
|
||||
override fun MinecraftServer.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val server = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["get_world"] = getWorld@{ params ->
|
||||
val world = server.getLevel(
|
||||
ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft")
|
||||
)
|
||||
) ?: return@getWorld DoubleValue.ZERO
|
||||
functions["get_world"] = getWorld@{ params ->
|
||||
val world =
|
||||
server.getLevel(
|
||||
ResourceKey.create(
|
||||
Registries.DIMENSION,
|
||||
params.getString(0).asIdentifierDefaultingNamespace(namespace = "minecraft"),
|
||||
),
|
||||
) ?: return@getWorld DoubleValue.ZERO
|
||||
|
||||
return@getWorld world
|
||||
world
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(world)
|
||||
.asWorldMoLangValue()
|
||||
}
|
||||
|
||||
map["broadcast"] = { params ->
|
||||
functions["broadcast"] = { params ->
|
||||
val message = params.getString(0)
|
||||
server.playerList.broadcastSystemMessage(message.text(), params.getBooleanOrNull(1) == true)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
map["get_player_by_uuid"] = getPlayerByUuid@{ params ->
|
||||
functions["get_player_by_uuid"] = getPlayerByUuid@{ params ->
|
||||
val uuid = UUID.fromString(params.getString(0))
|
||||
val player = server.playerList.getPlayer(uuid) ?: return@getPlayerByUuid DoubleValue.ZERO
|
||||
return@getPlayerByUuid player.asMoLangValue()
|
||||
player.asMoLangValue()
|
||||
}
|
||||
|
||||
map["get_player_by_username"] = getPlayerByUsername@{ params ->
|
||||
functions["get_player_by_username"] = getPlayerByUsername@{ params ->
|
||||
val username = params.getString(0)
|
||||
val player = server.playerList.getPlayerByName(username) ?: return@getPlayerByUsername DoubleValue.ZERO
|
||||
return@getPlayerByUsername player.asMoLangValue()
|
||||
player.asMoLangValue()
|
||||
}
|
||||
|
||||
map["data"] = { params ->
|
||||
functions["data"] = { params ->
|
||||
Cobblemon.molangData.load(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(0)
|
||||
params.getStringOrNull(0),
|
||||
)
|
||||
}
|
||||
|
||||
map["save_data"] = { params ->
|
||||
functions["save_data"] = { params ->
|
||||
Cobblemon.molangData.save(
|
||||
UUID(0L, 0L),
|
||||
params.getStringOrNull(index = 0)
|
||||
params.getStringOrNull(index = 0),
|
||||
)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
|
||||
// Maybe later...
|
||||
// map.put("stop") { params ->
|
||||
// functions.put("stop") { params ->
|
||||
// Cobblemon.LOGGER.warn("Server is being stopped from a MoLang script.")
|
||||
// server.stopServer()
|
||||
// }
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+28
-21
@@ -20,27 +20,34 @@ import net.minecraft.server.level.ServerPlayer
|
||||
object SpawnablePositionMoLangFunctions : AbstractMoLangFunctionHolder<SpawnablePosition>() {
|
||||
override fun SpawnablePosition.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val spawningContext = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>()
|
||||
val worldValue = spawningContext.world.registryAccess().registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(spawningContext.world).asWorldMoLangValue()
|
||||
val biomeValue = spawningContext.biomeHolder.asBiomeMoLangValue()
|
||||
map["biome"] = { _ -> biomeValue }
|
||||
map["world"] = { _ -> worldValue }
|
||||
map["light"] = { _ -> DoubleValue(spawningContext.light.toDouble()) }
|
||||
map["x"] = { _ -> DoubleValue(spawningContext.position.x.toDouble()) }
|
||||
map["y"] = { _ -> DoubleValue(spawningContext.position.y.toDouble()) }
|
||||
map["z"] = { _ -> DoubleValue(spawningContext.position.z.toDouble()) }
|
||||
map["moon_phase"] = { _ -> DoubleValue(spawningContext.moonPhase.toDouble()) }
|
||||
map["can_see_sky"] = { _ -> DoubleValue(spawningContext.canSeeSky) }
|
||||
map["sky_light"] = { _ -> DoubleValue(spawningContext.skyLight.toDouble()) }
|
||||
map["player"] = put@{ _ ->
|
||||
val causeEntity = spawningContext.cause.entity ?: return@put DoubleValue.ZERO
|
||||
if (causeEntity is ServerPlayer) {
|
||||
return@put causeEntity.asMoLangValue()
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
val worldValue =
|
||||
spawningContext
|
||||
.world
|
||||
.registryAccess()
|
||||
.registryOrThrow(Registries.DIMENSION)
|
||||
.wrapAsHolder(spawningContext.world)
|
||||
.asWorldMoLangValue()
|
||||
val biomeValue =
|
||||
spawningContext
|
||||
.biomeHolder
|
||||
.asBiomeMoLangValue()
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
functions["biome"] = { biomeValue }
|
||||
functions["world"] = { worldValue }
|
||||
functions["light"] = { DoubleValue(spawningContext.light.toDouble()) }
|
||||
functions["x"] = { DoubleValue(spawningContext.position.x.toDouble()) }
|
||||
functions["y"] = { DoubleValue(spawningContext.position.y.toDouble()) }
|
||||
functions["z"] = { DoubleValue(spawningContext.position.z.toDouble()) }
|
||||
functions["moon_phase"] = { DoubleValue(spawningContext.moonPhase.toDouble()) }
|
||||
functions["can_see_sky"] = { DoubleValue(spawningContext.canSeeSky) }
|
||||
functions["sky_light"] = { DoubleValue(spawningContext.skyLight.toDouble()) }
|
||||
functions["player"] = player@{
|
||||
val causeEntity = spawningContext.cause.entity ?: return@player DoubleValue.ZERO
|
||||
if (causeEntity is ServerPlayer) return@player causeEntity.asMoLangValue()
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
return map
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+17
-17
@@ -17,23 +17,23 @@ import com.cobblemon.mod.common.util.asArrayValue
|
||||
object SpeciesMoLangFunctions : AbstractMoLangFunctionHolder<Species>() {
|
||||
override fun Species.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
val species = this
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
"identifier" to { StringValue(species.resourceIdentifier.toString()) },
|
||||
"name" to { StringValue(species.name) },
|
||||
"primary_type" to { StringValue(species.primaryType.showdownId) },
|
||||
"secondary_type" to { StringValue(species.secondaryType?.showdownId ?: "null") },
|
||||
"experience_group" to { StringValue(species.experienceGroup.name) },
|
||||
"height" to { DoubleValue(species.height) },
|
||||
"weight" to { DoubleValue(species.weight) },
|
||||
"base_scale" to { DoubleValue(species.baseScale) },
|
||||
"hitbox_width" to { DoubleValue(species.hitbox.width) },
|
||||
"hitbox_height" to { DoubleValue(species.hitbox.height) },
|
||||
"hitbox_fixed" to { DoubleValue(species.hitbox.fixed) },
|
||||
"catch_rate" to { DoubleValue(species.catchRate) },
|
||||
"labels" to { species.labels.asArrayValue { StringValue(it) } },
|
||||
"has_label" to { params -> DoubleValue(species.labels.contains(params.getString(0))) }
|
||||
)
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
return map
|
||||
functions["identifier"] = { StringValue(species.resourceIdentifier.toString()) }
|
||||
functions["name"] = { StringValue(species.name) }
|
||||
functions["primary_type"] = { StringValue(species.primaryType.showdownId) }
|
||||
functions["secondary_type"] = { StringValue(species.secondaryType?.showdownId ?: "null") }
|
||||
functions["experience_group"] = { StringValue(species.experienceGroup.name) }
|
||||
functions["height"] = { DoubleValue(species.height) }
|
||||
functions["weight"] = { DoubleValue(species.weight) }
|
||||
functions["base_scale"] = { DoubleValue(species.baseScale) }
|
||||
functions["hitbox_width"] = { DoubleValue(species.hitbox.width) }
|
||||
functions["hitbox_height"] = { DoubleValue(species.hitbox.height) }
|
||||
functions["hitbox_fixed"] = { DoubleValue(species.hitbox.fixed) }
|
||||
functions["catch_rate"] = { DoubleValue(species.catchRate) }
|
||||
functions["labels"] = { species.labels.asArrayValue { StringValue(it) } }
|
||||
functions["has_label"] = { params -> DoubleValue(species.labels.contains(params.getString(0))) }
|
||||
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+96
-87
@@ -24,7 +24,6 @@ import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormParticlePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.sound.UnvalidatedPlaySoundS2CPacket
|
||||
import com.cobblemon.mod.common.util.*
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Holder
|
||||
import net.minecraft.core.registries.Registries
|
||||
@@ -46,85 +45,89 @@ import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets
|
||||
import net.minecraft.world.level.storage.loot.parameters.LootContextParams
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.UUID
|
||||
|
||||
object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
override fun Holder<Level>.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
|
||||
|
||||
val world = this.value()
|
||||
val map = mutableMapOf<String, (MoParams) -> Any>(
|
||||
)
|
||||
val functions = hashMapOf<String, (MoParams) -> Any>()
|
||||
|
||||
map["is_time_of_day"] = put@{ params ->
|
||||
functions["is_time_of_day"] = isTimeOfDay@{ params ->
|
||||
val timeOfDay =
|
||||
TimeRange.timeRanges[params.getString(0).lowercase()]
|
||||
?: return@isTimeOfDay DoubleValue.ZERO
|
||||
val time = world.dayTime % 24000
|
||||
|
||||
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
|
||||
?: return@put DoubleValue.ZERO
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(timeOfDay.contains(time.toInt()))
|
||||
DoubleValue(timeOfDay.contains(time.toInt()))
|
||||
}
|
||||
map["game_time"] = { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map["time_of_day"] = put@{
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(time.toDouble())
|
||||
}
|
||||
map["server"] = { _ -> server()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
map["is_raining_at"] = put@{ params ->
|
||||
functions["game_time"] = { DoubleValue(world.gameTime.toDouble()) }
|
||||
functions["time_of_day"] = { DoubleValue((world.dayTime % 24000).toDouble()) }
|
||||
functions["server"] = { server()?.asMoLangValue() ?: DoubleValue.ZERO }
|
||||
functions["is_raining_at"] = { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
|
||||
DoubleValue(world.isRainingAt(BlockPos(x, y, z)))
|
||||
}
|
||||
map["is_snowing_at"] = put@{ params ->
|
||||
functions["is_snowing_at"] = { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val blockPos = BlockPos(x, y, z)
|
||||
when {
|
||||
!world.isRaining -> return@put DoubleValue.ZERO
|
||||
!world.canSeeSky(blockPos) -> return@put DoubleValue.ZERO
|
||||
!world.isRaining -> {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
|
||||
!world.canSeeSky(blockPos) -> {
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
|
||||
(world.getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, blockPos).y > blockPos.y) -> {
|
||||
return@put DoubleValue.ZERO
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
|
||||
else -> {
|
||||
val biome = world.getBiome(blockPos).value() as Biome
|
||||
return@put DoubleValue(biome.getPrecipitationAt(blockPos) == Biome.Precipitation.SNOW)
|
||||
DoubleValue(biome.getPrecipitationAt(blockPos) == Biome.Precipitation.SNOW)
|
||||
}
|
||||
}
|
||||
}
|
||||
map["is_chunk_loaded_at"] = put@{ params ->
|
||||
functions["is_chunk_loaded_at"] = { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
return@put DoubleValue(world.isLoaded(BlockPos(x, y, z)))
|
||||
DoubleValue(world.isLoaded(BlockPos(x, y, z)))
|
||||
}
|
||||
map["is_thundering"] = { _ -> DoubleValue(world.isThundering) }
|
||||
map["is_raining"] = { _ -> DoubleValue(world.isRaining) }
|
||||
map["set_block"] = put@{ params ->
|
||||
functions["is_thundering"] = { DoubleValue(world.isThundering) }
|
||||
functions["is_raining"] = { DoubleValue(world.isRaining) }
|
||||
functions["set_block"] = setBlock@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val block = world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
val block =
|
||||
world.blockRegistry.get(params.getString(3).asIdentifierDefaultingNamespace())
|
||||
?: run {
|
||||
Cobblemon.LOGGER.error("Unknown block: ${params.getString(3)}")
|
||||
return@setBlock DoubleValue.ZERO
|
||||
}
|
||||
world.setBlock(BlockPos(x, y, z), block.defaultBlockState(), Block.UPDATE_ALL)
|
||||
}
|
||||
map["is_air"] = put@{ params ->
|
||||
functions["is_air"] = { params ->
|
||||
val x = params.getDouble(0).toInt()
|
||||
val y = params.getDouble(1).toInt()
|
||||
val z = params.getDouble(2).toInt()
|
||||
val blockState = world.getBlockState(BlockPos(x, y, z))
|
||||
return@put DoubleValue(blockState.isAir)
|
||||
DoubleValue(blockState.isAir)
|
||||
}
|
||||
map["get_block"] = put@{ params ->
|
||||
functions["get_block"] = { params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
val block = world.getBlockState(BlockPos(x, y, z)).block
|
||||
return@put world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
|
||||
world.blockRegistry.wrapAsHolder(block).asMoLangValue(Registries.BLOCK)
|
||||
}
|
||||
map["spawn_explosion"] = { params ->
|
||||
functions["spawn_explosion"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
@@ -136,32 +139,33 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
z,
|
||||
range,
|
||||
ExplosionInteraction.valueOf(
|
||||
params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name
|
||||
)
|
||||
params.getStringOrNull(4)?.uppercase() ?: ExplosionInteraction.TNT.name,
|
||||
),
|
||||
)
|
||||
}
|
||||
map["spawn_lightning"] = put@{ params ->
|
||||
functions["spawn_lightning"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val lightning = LightningBolt(EntityType.LIGHTNING_BOLT, world)
|
||||
lightning.setPos(x, y, z)
|
||||
world.addFreshEntity(lightning)
|
||||
return@put DoubleValue.ONE
|
||||
DoubleValue.ONE
|
||||
}
|
||||
// q.entity.world.spawn_bedrock_particles(effect, x, y, z, [player]) - sends to everyone nearby or just to the player if they're set.
|
||||
map["spawn_bedrock_particles"] = { params ->
|
||||
functions["spawn_bedrock_particles"] = { params ->
|
||||
val particle = params.getString(0).asResource()
|
||||
val x = params.getDouble(1)
|
||||
val y = params.getDouble(2)
|
||||
val z = params.getDouble(3)
|
||||
val player = params.getOrNull<MoValue>(4)?.let {
|
||||
when (it) {
|
||||
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
val player =
|
||||
params.getOrNull<MoValue>(4)?.let {
|
||||
when (it) {
|
||||
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
val pos = Vec3(x, y, z)
|
||||
|
||||
val packet = SpawnSnowstormParticlePacket(particle, pos)
|
||||
@@ -171,7 +175,7 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
packet.sendToPlayersAround(x, y, z, 64.0, world.dimension())
|
||||
}
|
||||
}
|
||||
map["spawn_pokemon"] = put@{ params ->
|
||||
functions["spawn_pokemon"] = spawnPokemon@{ params ->
|
||||
val x = params.getInt(0)
|
||||
val y = params.getInt(1)
|
||||
val z = params.getInt(2)
|
||||
@@ -179,49 +183,50 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
|
||||
val pos = BlockPos(x, y, z)
|
||||
|
||||
if (!Level.isInSpawnableBounds(pos)) {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
if (!Level.isInSpawnableBounds(pos)) return@spawnPokemon DoubleValue.ZERO
|
||||
|
||||
val pokemon = props.createEntity(world)
|
||||
pokemon.moveTo(pos, pokemon.yRot, pokemon.xRot)
|
||||
|
||||
if (world.addFreshEntity(pokemon)) {
|
||||
return@put pokemon.struct
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
if (world.addFreshEntity(pokemon)) return@spawnPokemon pokemon.struct
|
||||
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["spawn_npc"] = put@{ params ->
|
||||
functions["spawn_npc"] = spawnNpc@{ params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val npcClass = params.getStringOrNull(3)
|
||||
?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
val npcClass =
|
||||
params
|
||||
.getStringOrNull(3)
|
||||
?.let { NPCClasses.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
val level = params.getInt(4)
|
||||
if (npcClass == null) return@put DoubleValue.ZERO
|
||||
|
||||
if (npcClass == null) return@spawnNpc DoubleValue.ZERO
|
||||
|
||||
val npc = NPCEntity(world)
|
||||
npc.moveTo(x, y, z, npc.yRot, npc.xRot)
|
||||
npc.npc = npcClass
|
||||
npc.initialize(level)
|
||||
if (world.addFreshEntity(npc)) {
|
||||
return@put npc.asMoLangValue()
|
||||
}
|
||||
return@put DoubleValue.ZERO
|
||||
|
||||
if (world.addFreshEntity(npc)) return@spawnNpc npc.asMoLangValue()
|
||||
|
||||
DoubleValue.ZERO
|
||||
}
|
||||
map["play_sound_on_server"] = { params ->
|
||||
functions["play_sound_on_server"] = { params ->
|
||||
val sound = params.getString(0).asResource()
|
||||
val soundSource = params.getString(1).uppercase()
|
||||
val x = params.getDouble(2)
|
||||
val y = params.getDouble(3)
|
||||
val z = params.getDouble(4)
|
||||
val player = params.getOrNull<MoValue>(5)?.let {
|
||||
when (it) {
|
||||
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
val player =
|
||||
params.getOrNull<MoValue>(5)?.let {
|
||||
when (it) {
|
||||
is StringValue -> world.getPlayerByUUID(UUID.fromString(it.value))
|
||||
is ObjectValue<*> -> it.obj
|
||||
else -> null
|
||||
}
|
||||
} as? ServerPlayer
|
||||
val volume = params.getDoubleOrNull(6)?.toFloat() ?: 1.0f
|
||||
val pitch = params.getDoubleOrNull(7)?.toFloat() ?: 1.0f
|
||||
|
||||
@@ -233,29 +238,30 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
packet.sendToPlayersAround(x, y, z, 16.0, world.dimension())
|
||||
}
|
||||
}
|
||||
map["get_entities_around"] = put@{ params ->
|
||||
functions["get_entities_around"] = { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val range = params.getDouble(3) * 2
|
||||
val entities = world.getEntities(null, AABB.ofSize(Vec3(x, y, z), range, range, range))
|
||||
return@put entities
|
||||
entities
|
||||
.filterIsInstance<LivingEntity>()
|
||||
.map { it.asMostSpecificMoLangValue() }
|
||||
.asArrayValue()
|
||||
}
|
||||
map["is_healer_in_use"] = put@{ params ->
|
||||
functions["is_healer_in_use"] = isHealerInUse@{ params ->
|
||||
val pos = params.get<ArrayStruct>(0).asBlockPos()
|
||||
val healer = world.getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
|
||||
?: return@put DoubleValue.ONE
|
||||
return@put DoubleValue(healer.isInUse)
|
||||
val healer =
|
||||
world.getBlockEntity(pos, CobblemonBlockEntities.HEALING_MACHINE).orElse(null)
|
||||
?: return@isHealerInUse DoubleValue.ONE
|
||||
return@isHealerInUse DoubleValue(healer.isInUse)
|
||||
}
|
||||
|
||||
map["spawn_loot_table_items"] = put@{ params ->
|
||||
functions["spawn_loot_table_items"] = spawnLootTableItems@{ params ->
|
||||
val serverLevel = world as? ServerLevel
|
||||
if (serverLevel == null) {
|
||||
Cobblemon.LOGGER.warn("spawn_loot_table_items: world is not a ServerLevel")
|
||||
return@put DoubleValue.ZERO
|
||||
return@spawnLootTableItems DoubleValue.ZERO
|
||||
}
|
||||
val lootTableId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val x = params.getDouble(1)
|
||||
@@ -265,9 +271,11 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
val lootTableKey = ResourceKey.create(Registries.LOOT_TABLE, lootTableId)
|
||||
val lootTable = serverLevel.server.reloadableRegistries().getLootTable(lootTableKey)
|
||||
|
||||
val lootParams = LootParams.Builder(serverLevel)
|
||||
.withParameter(LootContextParams.ORIGIN, Vec3(x, y, z))
|
||||
.create(LootContextParamSets.COMMAND)
|
||||
val lootParams =
|
||||
LootParams
|
||||
.Builder(serverLevel)
|
||||
.withParameter(LootContextParams.ORIGIN, Vec3(x, y, z))
|
||||
.create(LootContextParamSets.COMMAND)
|
||||
|
||||
val items = lootTable.getRandomItems(lootParams)
|
||||
val spawnedItems = ArrayStruct(hashMapOf())
|
||||
@@ -285,9 +293,10 @@ object WorldMoLangFunctions : AbstractMoLangFunctionHolder<Holder<Level>>() {
|
||||
}
|
||||
}
|
||||
}
|
||||
return@put spawnedItems
|
||||
|
||||
spawnedItems
|
||||
}
|
||||
|
||||
return map
|
||||
return functions
|
||||
}
|
||||
}
|
||||
|
||||
+23
-7
@@ -8,8 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.orientation
|
||||
|
||||
import com.cobblemon.mod.common.util.math.QuatSpring
|
||||
import com.cobblemon.mod.common.util.math.geometry.toDegrees
|
||||
import com.cobblemon.mod.common.util.math.geometry.toRadians
|
||||
import com.cobblemon.mod.common.util.math.simpleSpringDamperQuat
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.util.Mth
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -26,6 +28,8 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
val FORWARDS: Vector3f = Vector3f(0.0f, 0.0f, -1.0f)
|
||||
val UP: Vector3f = Vector3f(0.0f, 1.0f, 0.0f)
|
||||
val LEFT: Vector3f = Vector3f(-1.0f, 0.0f, 0.0f)
|
||||
private const val TICK_DT = 1f / 20f // Minecraft server tick rate
|
||||
private const val RENDER_SMOOTH_HALFLIFE = 0.1f // seconds to close ~half the gap
|
||||
}
|
||||
|
||||
open var active: Boolean = false
|
||||
@@ -33,6 +37,8 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
var orientation: Matrix3f? = null
|
||||
private set
|
||||
|
||||
private val renderSpring = QuatSpring()
|
||||
|
||||
private var renderOrientationO: Matrix3f? = null
|
||||
private var renderOrientation: Matrix3f? = null
|
||||
|
||||
@@ -54,6 +60,8 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
orientation = null
|
||||
renderOrientationO = null
|
||||
renderOrientation = null
|
||||
renderSpring.quat.identity()
|
||||
renderSpring.angVel.set(0f, 0f, 0f)
|
||||
active = false
|
||||
}
|
||||
|
||||
@@ -77,18 +85,26 @@ open class OrientationController(val entity: LivingEntity) {
|
||||
return oldQuat
|
||||
}
|
||||
|
||||
/** Every tick move the current render orientation towards the last known true orientation **/
|
||||
fun tick() {
|
||||
if (!active || orientation == null) return
|
||||
renderOrientationO = renderOrientation
|
||||
val current = orientation
|
||||
val renderMatrix = this.renderOrientation ?: current
|
||||
val renderQuat = Quaternionf().setFromUnnormalized(renderMatrix)
|
||||
val targetQuat = Quaternionf().setFromUnnormalized(current)
|
||||
val dampingFactor = 0.66f // Smooth interpolation to reduce jitter of orientations recieved from the server.
|
||||
renderQuat.slerp(targetQuat, dampingFactor)
|
||||
val current = orientation!!
|
||||
val targetQuat = Quaternionf().setFromUnnormalized(current)
|
||||
|
||||
if (renderOrientation == null) {
|
||||
// First tick: snap the spring to the target instead of easing in from identity
|
||||
renderSpring.quat.set(targetQuat)
|
||||
renderSpring.angVel.set(0f, 0f, 0f)
|
||||
}
|
||||
|
||||
// Use a "quat spring" to smoothly move the render orientation towards the goal orientation.
|
||||
// It takes into account the orientations angular velocities and should be much more resilient
|
||||
// to sharp changes in goal orientation or late/missed packets.
|
||||
simpleSpringDamperQuat(renderSpring, targetQuat, 0.15f, TICK_DT)
|
||||
|
||||
val newRenderOrientation = Matrix3f()
|
||||
renderQuat.get(newRenderOrientation)
|
||||
renderSpring.quat.get(newRenderOrientation)
|
||||
renderOrientation = newRenderOrientation
|
||||
}
|
||||
|
||||
|
||||
@@ -269,7 +269,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("dive_ball", CatchRateModifiers.SUBMERGED_IN_WATER, waterDragValue = 0.99F)
|
||||
createDefault("nest_ball", CatchRateModifiers.NEST)
|
||||
createDefault("repeat_ball", CatchRateModifiers.REPEAT)
|
||||
createDefault("timer_ball", CatchRateModifiers.turnBased { turn -> (1F * turn * (1229F / 4096F)).coerceAtMost(4F) })
|
||||
createDefault("timer_ball", CatchRateModifiers.turnBased { turn -> (1F + turn * (1229F / 4096F)).coerceAtMost(4F) })
|
||||
createDefault("luxury_ball", effects = listOf(FriendshipEarningBoostEffect(2F)))
|
||||
createDefault("premier_ball")
|
||||
createDefault("dusk_ball", CatchRateModifiers.LIGHT_LEVEL)
|
||||
@@ -278,7 +278,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("cherish_ball")
|
||||
createDefault("park_ball", CatchRateModifiers.PARK)
|
||||
createDefault("dream_ball", CatchRateModifiers.statusBoosting(4F, Statuses.SLEEP))
|
||||
createDefault("beast_ball", LabelModifier(5F, true, CobblemonPokemonLabels.ULTRA_BEAST)/*, LabelModifier(0.1F, false, CobblemonPokemonLabels.ULTRA_BEAST))*/)
|
||||
createDefault("beast_ball", DynamicMultiplierModifier({ _, pokemon -> if (pokemon.hasLabels(CobblemonPokemonLabels.ULTRA_BEAST)) 5F else 0.1F }) { _, _ -> true })
|
||||
createDefault("ancient_poke_ball", ancient = true)
|
||||
createDefault("ancient_citrine_ball", ancient = true)
|
||||
createDefault("ancient_verdant_ball", ancient = true)
|
||||
@@ -297,13 +297,15 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
|
||||
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
|
||||
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
|
||||
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
|
||||
if (increment <= 0) //these affects are only meant to affect positive gains
|
||||
if (!event.earned) {
|
||||
return@subscribe
|
||||
if (increment <= 1F) {
|
||||
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
|
||||
.forEach { increment *= it.multiplier }
|
||||
}
|
||||
var increment = (event.newFriendship - event.pokemon.friendship).toFloat()
|
||||
if (increment <= 0) {
|
||||
return@subscribe
|
||||
}
|
||||
event.pokemon.caughtBall.effects.filterIsInstance<FriendshipEarningBoostEffect>()
|
||||
.forEach { increment *= it.multiplier }
|
||||
event.newFriendship = event.pokemon.friendship + increment.roundToInt()
|
||||
}
|
||||
}
|
||||
@@ -338,7 +340,6 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
ancient: Boolean = false
|
||||
): PokeBall {
|
||||
val identifier = cobblemonResource(name)
|
||||
//val finalModifiers = if (appendUltraBeastPenalty) modifiers + listOf(LabelModifier(0.1F, true, CobblemonPokemonLabels.ULTRA_BEAST)) else modifiers
|
||||
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient)
|
||||
this.defaults[identifier] = pokeball
|
||||
return pokeball
|
||||
|
||||
+13
-15
@@ -54,22 +54,20 @@ object CatchRateModifiers {
|
||||
|
||||
/**
|
||||
* Used by [PokeBalls.LOVE_BALL].
|
||||
* Boosts the catch rate if the Pokémon is of the same species and opposite gender
|
||||
* Boosts the catch rate 2.5× if any of the player's active Pokémon is of the opposite gender,
|
||||
* or 8× if that Pokémon is also of the same species.
|
||||
*/
|
||||
val LOVE = BattleModifier { _, playerPokemon, pokemon ->
|
||||
if (
|
||||
pokemon.gender != Gender.GENDERLESS
|
||||
&& playerPokemon.mapNotNull { it.battlePokemon?.originalPokemon }
|
||||
.any {
|
||||
it.gender != Gender.GENDERLESS &&
|
||||
it.species.name.equals(pokemon.species.name, true) &&
|
||||
it.gender != pokemon.gender
|
||||
}
|
||||
) {
|
||||
if (playerPokemon.any { it.battlePokemon?.originalPokemon?.species?.resourceIdentifier == pokemon.species.resourceIdentifier }) 8F else 2.5F
|
||||
if (pokemon.gender == Gender.GENDERLESS) {
|
||||
return@BattleModifier 1F
|
||||
}
|
||||
else {
|
||||
1F
|
||||
val oppositeGender = playerPokemon
|
||||
.mapNotNull { it.battlePokemon?.originalPokemon }
|
||||
.filter { it.gender != Gender.GENDERLESS && it.gender != pokemon.gender }
|
||||
when {
|
||||
oppositeGender.any { it.species.resourceIdentifier == pokemon.species.resourceIdentifier } -> 8F
|
||||
oppositeGender.isNotEmpty() -> 2.5F
|
||||
else -> 1F
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +79,7 @@ object CatchRateModifiers {
|
||||
* 1× otherwise.
|
||||
*/
|
||||
val MOON_PHASES: CatchRateModifier = WorldStateModifier { _, entity ->
|
||||
if (entity.level().gameTime in 12000..24000)
|
||||
if (entity.level().dayTime % 24000 < 12000)
|
||||
return@WorldStateModifier 1F
|
||||
when (entity.level().moonPhase) {
|
||||
2, 6 -> 1.5F
|
||||
@@ -110,7 +108,7 @@ object CatchRateModifiers {
|
||||
* Used by [PokeBalls.SAFARI_BALL].
|
||||
* Checks if the target is not battling, if true boost by *1.5
|
||||
*/
|
||||
val SAFARI = WorldStateModifier { _, entity -> if (!entity.isBattling) 1.5F else 1F }
|
||||
val SAFARI = MultiplierModifier(1.5f) { _, poke -> !poke.entity?.isBattling!! }
|
||||
|
||||
/**
|
||||
* Used by [PokeBalls.PARK_BALL].
|
||||
|
||||
@@ -13,6 +13,10 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.WeightedChoiceSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
@@ -72,6 +76,57 @@ abstract class AbstractPokedexManager {
|
||||
} ?: PokedexEntryProgress.UNREGISTERED
|
||||
}
|
||||
|
||||
fun getHighestKnowledgeFor(entry: PokedexEntry, form: PokedexForm?): PokedexEntryProgress {
|
||||
if (form == null) return getHighestKnowledgeFor(entry)
|
||||
|
||||
val speciesRecord = getSpeciesRecord(entry.speciesId) ?: return PokedexEntryProgress.UNREGISTERED
|
||||
val hasAllAspects = entry.conditionAspects.all(speciesRecord::hasAspect)
|
||||
if (!hasAllAspects) {
|
||||
return PokedexEntryProgress.UNREGISTERED
|
||||
}
|
||||
|
||||
return speciesRecord.getFormRecord(form.displayForm)?.knowledge ?: PokedexEntryProgress.UNREGISTERED
|
||||
}
|
||||
|
||||
fun getHighestKnowledgeFor(entry: PokedexEntry, form: PokedexForm?, aspects: Iterable<String>): PokedexEntryProgress {
|
||||
val formKnowledge = getHighestKnowledgeFor(entry, form)
|
||||
|
||||
if (formKnowledge == PokedexEntryProgress.UNREGISTERED) return PokedexEntryProgress.UNREGISTERED
|
||||
|
||||
val seenAspects = getSeenAspects(entry)
|
||||
|
||||
// Aspects that select one of the species' own forms are already covered by the form knowledge check above,
|
||||
// so exempt them (and every other value of the same feature, since defaults like "phony" never appear on a form directly).
|
||||
val species = PokemonSpecies.getByIdentifier(entry.speciesId)
|
||||
val formAspects = species?.forms?.flatMapTo(hashSetOf()) { it.aspects } ?: emptySet()
|
||||
val formSelectingFeatureAspects = species?.let(SpeciesFeatures::getFeaturesFor).orEmpty()
|
||||
.mapNotNull { provider ->
|
||||
val allAspects: Collection<String> = when (provider) {
|
||||
is ChoiceSpeciesFeatureProvider -> provider.getAllAspects()
|
||||
is WeightedChoiceSpeciesFeatureProvider -> provider.getAllAspects().keys
|
||||
else -> return@mapNotNull null
|
||||
}
|
||||
allAspects.takeIf { it.any { aspect -> aspect in formAspects } }
|
||||
}
|
||||
.flatten()
|
||||
.toHashSet()
|
||||
|
||||
val allSeenAspects = aspects
|
||||
.filter { aspect ->
|
||||
if (Dexes.equivalentFormsLookup[aspect] != null) return@filter false
|
||||
if (aspect in formAspects || aspect in formSelectingFeatureAspects) return@filter false
|
||||
if (aspect.uppercase() in setOf(Gender.MALE.name, Gender.FEMALE.name, Gender.GENDERLESS.name)) return@filter false
|
||||
if ("shiny".equals(aspect, ignoreCase = true)) return@filter false
|
||||
|
||||
return@filter true
|
||||
}
|
||||
.all { aspect -> seenAspects.contains(aspect) }
|
||||
|
||||
if (!allSeenAspects) return PokedexEntryProgress.UNREGISTERED
|
||||
|
||||
return formKnowledge
|
||||
}
|
||||
|
||||
fun getEncounteredForms(entry: PokedexEntry) : List<PokedexForm> {
|
||||
return getFormsWithKnowledge(entry, PokedexEntryProgress.SEEN)
|
||||
}
|
||||
|
||||
@@ -10,6 +10,9 @@ package com.cobblemon.mod.common.api.pokedex
|
||||
|
||||
import com.cobblemon.mod.common.api.data.JsonDataRegistry
|
||||
import com.cobblemon.mod.common.api.pokedex.def.PokedexDef
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.net.messages.client.data.PokedexDexSyncPacket
|
||||
import com.cobblemon.mod.common.util.adapters.CodecBackedAdapter
|
||||
@@ -40,6 +43,17 @@ object Dexes : JsonDataRegistry<PokedexDef> {
|
||||
//Maps a dex id to its PokedexDef
|
||||
val dexEntryMap = linkedMapOf<ResourceLocation, PokedexDef>()
|
||||
|
||||
val equivalentForms = mapOf(
|
||||
"galar" to listOf("region-bias-galar", "bias-galar", "galar-bias", "galarian", "galar"),
|
||||
"alola" to listOf("region-bias-alola", "bias-alola", "alola-bias", "alolan", "alola"),
|
||||
"hisui" to listOf("region-bias-hisui", "bias-hisui", "hisui-bias", "hisuian", "hisui"),
|
||||
"paldea" to listOf("region-bias-paldea", "bias-paldea", "paldea-bias", "paldean", "paldea"),
|
||||
)
|
||||
|
||||
val equivalentFormsLookup = equivalentForms.flatMap { (key, values) ->
|
||||
values.map { it to key }
|
||||
}.toMap()
|
||||
|
||||
override fun reload(data: Map<ResourceLocation, PokedexDef>) {
|
||||
dexEntryMap.clear()
|
||||
data.entries.sortedBy { it.value.sortOrder }.forEach { (id, def) -> dexEntryMap[id] = def }
|
||||
@@ -49,4 +63,41 @@ object Dexes : JsonDataRegistry<PokedexDef> {
|
||||
override fun sync(player: ServerPlayer) {
|
||||
PokedexDexSyncPacket(dexEntryMap.values).sendToPlayer(player)
|
||||
}
|
||||
|
||||
fun findDexEntryBySpeciesId(regionId: ResourceLocation, speciesId: ResourceLocation): PokedexEntry? {
|
||||
return dexEntryMap[regionId]?.getEntries()?.firstOrNull { it -> it.speciesId == speciesId }
|
||||
}
|
||||
|
||||
fun findDexEntryBySpeciesId(speciesId: ResourceLocation): PokedexEntry? {
|
||||
return findDexEntryBySpeciesId(cobblemonResource("national"), speciesId)
|
||||
}
|
||||
|
||||
fun findRootDexEntryBySpeciesId(speciesId: ResourceLocation, formName: String): Pair<PokedexEntry, PokedexForm> {
|
||||
var rootSpecies = PokemonSpecies.getByIdentifier(speciesId)!!
|
||||
val initialFormEquivalents = getFormEquivalents(formName)
|
||||
var rootForm = rootSpecies.forms.firstOrNull { initialFormEquivalents.contains(it.name.lowercase()) }
|
||||
?: rootSpecies.standardForm
|
||||
|
||||
var preEvolution = rootForm.preEvolution
|
||||
while (preEvolution != null) {
|
||||
rootSpecies = preEvolution.species
|
||||
rootForm = preEvolution.resolveForm(rootForm.aspects.toSet())
|
||||
preEvolution = rootForm.preEvolution
|
||||
}
|
||||
|
||||
val entry = findDexEntryBySpeciesId(rootSpecies.resourceIdentifier)
|
||||
?: throw RuntimeException("Root dex entry not found")
|
||||
|
||||
val formEquivalents = getFormEquivalents(rootForm.name)
|
||||
val form =
|
||||
entry.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) } ?:
|
||||
entry.forms.first { it.displayForm.equals("Normal", ignoreCase = true) }
|
||||
|
||||
return entry to form
|
||||
}
|
||||
|
||||
fun getFormEquivalents(formName: String): List<String> {
|
||||
val key = equivalentFormsLookup[formName.lowercase()]
|
||||
return equivalentForms[key] ?: listOf(formName.lowercase())
|
||||
}
|
||||
}
|
||||
@@ -42,7 +42,7 @@ class PokedexManager(
|
||||
}
|
||||
|
||||
// Java interop isn't great lol
|
||||
@Deprecated("Use encounter(PokedexEntityData) instead")
|
||||
@Deprecated("Use obtain(Pokemon) instead")
|
||||
fun catch(pokemon: Pokemon) {
|
||||
obtain(pokemon)
|
||||
}
|
||||
|
||||
@@ -121,6 +121,7 @@ class SpeciesDexRecord {
|
||||
|
||||
fun addAspects(addedAspects: Set<String>) {
|
||||
aspects.addAll(addedAspects)
|
||||
pokedexManager.onSpeciesRecordUpdated(this)
|
||||
}
|
||||
|
||||
/** Returns true if the given Pokémon contains new information. Internal because it's only to be called from [FormDexRecord.wouldBeDifferent]. */
|
||||
|
||||
@@ -44,6 +44,9 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.types.ElementalType
|
||||
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
|
||||
import com.cobblemon.mod.common.net.messages.client.data.FormEvolutionOverride
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionEntry
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
|
||||
import com.cobblemon.mod.common.pokemon.FormData
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
@@ -128,7 +131,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
|
||||
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
|
||||
.registerTypeAdapter(MobEffect::class.java, RegistryElementAdapter<MobEffect>(BuiltInRegistries::MOB_EFFECT))
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
|
||||
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter())
|
||||
.registerTypeAdapter(RidingBehaviourSettings::class.java, RidingBehaviourSettingsAdapter)
|
||||
.registerTypeAdapter(RideSoundSettingsList::class.java, RideSoundSettingsListAdapter)
|
||||
.registerTypeAdapter(ObtainableItemCondition::class.java, ObtainableItemConditionAdapter)
|
||||
@@ -243,6 +246,18 @@ object PokemonSpecies : JsonDataRegistry<Species> {
|
||||
|
||||
override fun sync(player: ServerPlayer) {
|
||||
SpeciesRegistrySyncPacket(species.toList()).sendToPlayer(player)
|
||||
// Sent after the species themselves so every species is already registered by the time this is decoded.
|
||||
val evolutionEntries = species.map { singleSpecies ->
|
||||
val formOverrides = singleSpecies.forms.mapNotNull { form ->
|
||||
if (form.ownEvolutions == null && form.ownPreEvolution == null) {
|
||||
null
|
||||
} else {
|
||||
FormEvolutionOverride(form.name, form.ownEvolutions, form.ownPreEvolution)
|
||||
}
|
||||
}
|
||||
SpeciesEvolutionEntry(singleSpecies.resourceIdentifier, singleSpecies.evolutions, singleSpecies.preEvolution, formOverrides)
|
||||
}
|
||||
SpeciesEvolutionSyncPacket(evolutionEntries).sendToPlayer(player)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,6 +25,8 @@ interface PreEvolution {
|
||||
|
||||
val form: FormData
|
||||
|
||||
fun resolveForm(extraAspects: Set<String> = emptySet()): FormData = form
|
||||
|
||||
companion object {
|
||||
|
||||
fun of(species: Species, form: FormData = species.standardForm): PreEvolution = StandardPreEvolution(species, form)
|
||||
|
||||
@@ -11,6 +11,8 @@ package com.cobblemon.mod.common.api.pokemon.requirement
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.ContextEvolution
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import java.util.WeakHashMap
|
||||
|
||||
/**
|
||||
* Represents a universal requirement suitable for various checks on pokemon..
|
||||
@@ -32,4 +34,11 @@ fun interface Requirement {
|
||||
*/
|
||||
fun check(pokemon: Pokemon): Boolean
|
||||
|
||||
val pokedexIcon: ResourceLocation? get() = pokedexIconOverrides[this]
|
||||
val pokedexTooltip: String? get() = pokedexTooltipOverrides[this]
|
||||
|
||||
companion object {
|
||||
val pokedexIconOverrides = WeakHashMap<Requirement, ResourceLocation>()
|
||||
val pokedexTooltipOverrides = WeakHashMap<Requirement, String>()
|
||||
}
|
||||
}
|
||||
@@ -54,8 +54,8 @@ enum class Stats(override val identifier: ResourceLocation, override val display
|
||||
"hp" -> HP
|
||||
"atk", "attack" -> ATTACK // Hyper Cutter states the full stat name "Attack"
|
||||
"def", "defense", "defence" -> DEFENCE // Big Pecks states the full stat name "Defense"
|
||||
"spa" -> SPECIAL_ATTACK
|
||||
"spd" -> SPECIAL_DEFENCE
|
||||
"spa", "special_attack" -> SPECIAL_ATTACK
|
||||
"spd", "special_defence", "special_defense" -> SPECIAL_DEFENCE
|
||||
"spe", "speed" -> SPEED
|
||||
"evasion" -> EVASION
|
||||
else -> ACCURACY
|
||||
|
||||
+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.api.riding.util
|
||||
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.phys.AABB
|
||||
import kotlin.math.floor
|
||||
|
||||
/**
|
||||
* Walks every block position inside [searchBox] and turns each solid block into one
|
||||
* full-cube [FieldPrimitive].
|
||||
*
|
||||
* @author Jackson
|
||||
* @since July 27th, 2026
|
||||
*/
|
||||
|
||||
fun collectPrimitivesFromBlocks(
|
||||
level: BlockGetter,
|
||||
searchBox: AABB,
|
||||
shape: PrimitiveShape,
|
||||
includeFluids: Boolean = false
|
||||
): List<FieldPrimitive> {
|
||||
val primitives = mutableListOf<FieldPrimitive>()
|
||||
|
||||
val minX = floor(searchBox.minX).toInt()
|
||||
val maxX = floor(searchBox.maxX).toInt()
|
||||
val minY = floor(searchBox.minY).toInt()
|
||||
val maxY = floor(searchBox.maxY).toInt()
|
||||
val minZ = floor(searchBox.minZ).toInt()
|
||||
val maxZ = floor(searchBox.maxZ).toInt()
|
||||
|
||||
val pos = BlockPos.MutableBlockPos()
|
||||
for (x in minX..maxX) {
|
||||
for (y in minY..maxY) {
|
||||
for (z in minZ..maxZ) {
|
||||
pos.set(x, y, z)
|
||||
val state = level.getBlockState(pos)
|
||||
|
||||
val isSolid = !state.getCollisionShape(level, pos).isEmpty
|
||||
val isFluid = includeFluids && !state.fluidState.isEmpty
|
||||
if (!isSolid && !isFluid) continue
|
||||
|
||||
primitives.add(
|
||||
FieldPrimitive(
|
||||
shape = shape,
|
||||
centerX = x + 0.5f, centerY = y + 0.5f, centerZ = z + 0.5f,
|
||||
halfWidth = 0.5f, halfHeight = 0.5f, halfDepth = 0.5f
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return primitives
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.riding.util
|
||||
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
/**
|
||||
* Helpful Functions when working with signed distance fields/functions.
|
||||
* https://iquilezles.org/articles/distfunctions/
|
||||
*
|
||||
* @author Jackson
|
||||
* @since July 27th, 2026
|
||||
*/
|
||||
|
||||
/** Which distance formula a primitive uses. */
|
||||
enum class PrimitiveShape {
|
||||
BOX,
|
||||
ROUNDED_BOX,
|
||||
ELLIPSOID
|
||||
}
|
||||
|
||||
/** Tunable constants for the field. */
|
||||
data class DistanceFieldTuning(
|
||||
val smoothingRange: Float = 0.5f,
|
||||
val roundingRadius: Float = 0.08f
|
||||
)
|
||||
|
||||
/** A single shape in the field - a center position, half-extents, and which formula to use. */
|
||||
data class FieldPrimitive(
|
||||
val shape: PrimitiveShape,
|
||||
val centerX: Float, val centerY: Float, val centerZ: Float,
|
||||
val halfWidth: Float, val halfHeight: Float, val halfDepth: Float
|
||||
)
|
||||
|
||||
/** Fast approximate 1/sqrt(x) - the Quake III trick, cheaper than a real sqrt + divide. */
|
||||
fun fastInverseSqrt(value: Float): Float {
|
||||
var x = value
|
||||
val halfX = 0.5f * x
|
||||
var bits = java.lang.Float.floatToIntBits(x)
|
||||
bits = 0x5f3759df - (bits shr 1)
|
||||
x = java.lang.Float.intBitsToFloat(bits)
|
||||
x *= (1.5f - halfX * x * x)
|
||||
return x
|
||||
}
|
||||
|
||||
/** Exact distance from a point to an axis-aligned box. Inputs are relative to the box's own center. */
|
||||
fun boxDistance(relX: Float, relY: Float, relZ: Float, halfWidth: Float, halfHeight: Float, halfDepth: Float): Float {
|
||||
val qx = abs(relX) - halfWidth
|
||||
val qy = abs(relY) - halfHeight
|
||||
val qz = abs(relZ) - halfDepth
|
||||
val ox = max(qx, 0f)
|
||||
val oy = max(qy, 0f)
|
||||
val oz = max(qz, 0f)
|
||||
val outside = sqrt(ox * ox + oy * oy + oz * oz)
|
||||
val inside = min(max(qx, max(qy, qz)), 0f)
|
||||
return outside + inside
|
||||
}
|
||||
|
||||
/** Same as boxDistance but with rounded corners/edges https://iquilezles.org/articles/distfunctions/ */
|
||||
fun roundedBoxDistance(relX: Float, relY: Float, relZ: Float, halfWidth: Float, halfHeight: Float, halfDepth: Float, roundingRadius: Float): Float =
|
||||
boxDistance(relX, relY, relZ, halfWidth - roundingRadius, halfHeight - roundingRadius, halfDepth - roundingRadius) - roundingRadius
|
||||
|
||||
/** Approximate (lower-bound) distance to an ellipsoid https://iquilezles.org/articles/ellipsoids/. */
|
||||
fun ellipsoidDistance(relX: Float, relY: Float, relZ: Float, halfWidth: Float, halfHeight: Float, halfDepth: Float): Float {
|
||||
val px = relX / halfWidth
|
||||
val py = relY / halfHeight
|
||||
val pz = relZ / halfDepth
|
||||
val inverseMagnitude = fastInverseSqrt(px * px + py * py + pz * pz)
|
||||
val trueDistance = sqrt(relX * relX + relY * relY + relZ * relZ)
|
||||
return trueDistance * (1.0f - inverseMagnitude)
|
||||
}
|
||||
|
||||
/** Distance from a point to one primitive, dispatched by its shape. */
|
||||
fun distanceToPrimitive(primitive: FieldPrimitive, sampleX: Float, sampleY: Float, sampleZ: Float, tuning: DistanceFieldTuning): Float {
|
||||
val relX = sampleX - primitive.centerX
|
||||
val relY = sampleY - primitive.centerY
|
||||
val relZ = sampleZ - primitive.centerZ
|
||||
return when (primitive.shape) {
|
||||
PrimitiveShape.BOX -> boxDistance(relX, relY, relZ, primitive.halfWidth, primitive.halfHeight, primitive.halfDepth)
|
||||
PrimitiveShape.ROUNDED_BOX -> roundedBoxDistance(relX, relY, relZ, primitive.halfWidth, primitive.halfHeight, primitive.halfDepth, tuning.roundingRadius)
|
||||
PrimitiveShape.ELLIPSOID -> ellipsoidDistance(relX, relY, relZ, primitive.halfWidth, primitive.halfHeight, primitive.halfDepth)
|
||||
}
|
||||
}
|
||||
|
||||
///** Normalized quadratic smooth-min https://iquilezles.org/articles/smin/ */
|
||||
//fun smoothMinimum(a: Float, b: Float, smoothingRange: Float): Float {
|
||||
// if (smoothingRange <= 0.001f) return min(a, b)
|
||||
// val k = smoothingRange * 4.0f
|
||||
// val h = (0.5f + 0.5f * (b - a) / k).coerceIn(0f, 1f)
|
||||
// return b + (a - b) * h - k * h * (1f - h)
|
||||
//}
|
||||
//
|
||||
///** Folds every primitive's distance together into one field value at a point. */
|
||||
//fun sampleField(primitives: List<FieldPrimitive>, sampleX: Float, sampleY: Float, sampleZ: Float, tuning: DistanceFieldTuning): Float {
|
||||
// var result = Float.POSITIVE_INFINITY
|
||||
// for (primitive in primitives) {
|
||||
// val distance = distanceToPrimitive(primitive, sampleX, sampleY, sampleZ, tuning)
|
||||
// result = if (result == Float.POSITIVE_INFINITY) distance else smoothMinimum(result, distance, tuning.smoothingRange)
|
||||
// }
|
||||
// return result
|
||||
//}
|
||||
|
||||
/** Normalized quadratic smooth-min https://iquilezles.org/articles/smin/ */
|
||||
fun smoothMinimum(a: Float, b: Float, smoothingRange: Float): Float {
|
||||
if (smoothingRange <= 0.001f) return min(a, b)
|
||||
val k = smoothingRange * 4.0f
|
||||
val h = (0.5f + 0.5f * (b - a) / k).coerceIn(0f, 1f)
|
||||
return b + (a - b) * h - k * h * (1f - h)
|
||||
}
|
||||
|
||||
/** Folds nearby primitive distances together into one field value at a point. */
|
||||
fun sampleField(primitives: List<FieldPrimitive>, sampleX: Float, sampleY: Float, sampleZ: Float, tuning: DistanceFieldTuning): Float {
|
||||
if (primitives.isEmpty()) return Float.POSITIVE_INFINITY
|
||||
|
||||
val distances = FloatArray(primitives.size) { distanceToPrimitive(primitives[it], sampleX, sampleY, sampleZ, tuning) }
|
||||
val minDistance = distances.min()
|
||||
|
||||
if (tuning.smoothingRange <= 0.001f) return minDistance
|
||||
|
||||
// Anything more than a few blend-widths past the closest primitive is cut - its
|
||||
// contribution would be negligible in a correct blend anyway, but left in, it still
|
||||
// drags the chained result down the more of them there are.
|
||||
val cutoff = tuning.smoothingRange * 3f
|
||||
|
||||
var result = Float.POSITIVE_INFINITY
|
||||
for (distance in distances) {
|
||||
if (distance - minDistance > cutoff) continue
|
||||
result = if (result == Float.POSITIVE_INFINITY) distance else smoothMinimum(result, distance, tuning.smoothingRange)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
/** Finite-difference gradient of the field at a point - points away from the surface, magnitude ~1 for a clean field. */
|
||||
fun estimateGradient(primitives: List<FieldPrimitive>, point: Vec3, tuning: DistanceFieldTuning, epsilon: Float = 0.05f): Vec3 {
|
||||
val px = point.x.toFloat()
|
||||
val py = point.y.toFloat()
|
||||
val pz = point.z.toFloat()
|
||||
|
||||
val dx = sampleField(primitives, px + epsilon, py, pz, tuning) - sampleField(primitives, px - epsilon, py, pz, tuning)
|
||||
val dy = sampleField(primitives, px, py + epsilon, pz, tuning) - sampleField(primitives, px, py - epsilon, pz, tuning)
|
||||
val dz = sampleField(primitives, px, py, pz + epsilon, tuning) - sampleField(primitives, px, py, pz - epsilon, tuning)
|
||||
|
||||
val gradient = Vec3(dx.toDouble(), dy.toDouble(), dz.toDouble())
|
||||
return if (gradient.lengthSqr() > 1e-8) gradient.normalize() else Vec3(0.0, 1.0, 0.0)
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* 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.riding.util
|
||||
|
||||
import net.minecraft.core.particles.DustParticleOptions
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.phys.AABB
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import org.joml.Vector3f
|
||||
|
||||
/** One shell to draw - a target field value, a color, a particle size. */
|
||||
data class DistanceFieldLayer(
|
||||
val isoValue: Float,
|
||||
val color: Vector3f,
|
||||
val particleSize: Float = 1.0f
|
||||
)
|
||||
|
||||
/**
|
||||
* Marches a grid of vertical columns through the field around [center] and spawns a
|
||||
* particle everywhere it crosses one of [layers]' target values. Server-side only.
|
||||
*/
|
||||
fun visualizeDistanceField(
|
||||
referenceEntity: Entity,
|
||||
center: Vec3,
|
||||
layers: List<DistanceFieldLayer>,
|
||||
shape: PrimitiveShape = PrimitiveShape.BOX,
|
||||
horizontalRadius: Double = 4.0,
|
||||
verticalRange: Double = 4.0,
|
||||
sampleSpacing: Double = 0.5,
|
||||
marchStep: Double = 0.1,
|
||||
tuning: DistanceFieldTuning = DistanceFieldTuning()
|
||||
) {
|
||||
val level = referenceEntity.level()
|
||||
if (level !is ServerLevel) return
|
||||
|
||||
val searchBox = AABB(
|
||||
center.x - horizontalRadius, center.y - verticalRange, center.z - horizontalRadius,
|
||||
center.x + horizontalRadius, center.y, center.z + horizontalRadius
|
||||
)
|
||||
|
||||
val primitives = collectPrimitivesFromBlocks(level, searchBox, shape, includeFluids = true)
|
||||
if (primitives.isEmpty()) return
|
||||
|
||||
var x = -horizontalRadius
|
||||
while (x <= horizontalRadius) {
|
||||
var z = -horizontalRadius
|
||||
while (z <= horizontalRadius) {
|
||||
marchColumn(level, primitives, center, x, z, verticalRange, marchStep, layers, tuning)
|
||||
z += sampleSpacing
|
||||
}
|
||||
x += sampleSpacing
|
||||
}
|
||||
}
|
||||
|
||||
private fun marchColumn(
|
||||
level: ServerLevel,
|
||||
primitives: List<FieldPrimitive>,
|
||||
center: Vec3,
|
||||
offsetX: Double,
|
||||
offsetZ: Double,
|
||||
verticalRange: Double,
|
||||
marchStep: Double,
|
||||
layers: List<DistanceFieldLayer>,
|
||||
tuning: DistanceFieldTuning
|
||||
) {
|
||||
val previousValue = FloatArray(layers.size) { Float.NaN }
|
||||
|
||||
var y = verticalRange
|
||||
while (y >= -verticalRange) {
|
||||
val worldX = (center.x + offsetX).toFloat()
|
||||
val worldY = (center.y + y).toFloat()
|
||||
val worldZ = (center.z + offsetZ).toFloat()
|
||||
val fieldValue = sampleField(primitives, worldX, worldY, worldZ, tuning)
|
||||
|
||||
for (layerIndex in layers.indices) {
|
||||
val layer = layers[layerIndex]
|
||||
val previous = previousValue[layerIndex]
|
||||
|
||||
if (!previous.isNaN() && crossedTarget(previous, fieldValue, layer.isoValue)) {
|
||||
val t = (layer.isoValue - previous) / (fieldValue - previous)
|
||||
val crossingY = (y + marchStep) - marchStep * t
|
||||
spawnLayerParticle(level, center.add(offsetX, crossingY, offsetZ), layer)
|
||||
}
|
||||
|
||||
previousValue[layerIndex] = fieldValue
|
||||
}
|
||||
|
||||
y -= marchStep
|
||||
}
|
||||
}
|
||||
|
||||
private fun crossedTarget(previous: Float, current: Float, target: Float): Boolean =
|
||||
(previous - target) * (current - target) < 0f
|
||||
|
||||
private fun spawnLayerParticle(level: ServerLevel, pos: Vec3, layer: DistanceFieldLayer) {
|
||||
val particle = DustParticleOptions(layer.color, layer.particleSize)
|
||||
level.sendParticles(particle, pos.x, pos.y, pos.z, 1, 0.0, 0.0, 0.0, 0.0)
|
||||
}
|
||||
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.spawning
|
||||
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange.Companion.timeRanges
|
||||
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
|
||||
|
||||
/**
|
||||
@@ -49,4 +51,25 @@ class TimeRange : IntRanges {
|
||||
|
||||
constructor() : super()
|
||||
constructor(vararg ranges: IntRange) : super(*ranges)
|
||||
|
||||
fun displayName(): MutableComponent? {
|
||||
val key = timeRanges.entries.firstOrNull { entry -> this == entry.value }?.key ?: return null
|
||||
return lang("ui.time_range.$key")
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
if (other !is TimeRange) return false
|
||||
|
||||
if (other.ranges.size != this.ranges.size) return false
|
||||
other.ranges.forEachIndexed { index, otherRange ->
|
||||
if (this.ranges[index] != otherRange) return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return ranges.hashCode()
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,8 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.spawning.condition
|
||||
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.world.level.Level
|
||||
|
||||
/**
|
||||
@@ -42,4 +44,7 @@ enum class MoonPhase {
|
||||
fun ofWorld(world: Level): MoonPhase = VALUES[world.moonPhase]
|
||||
}
|
||||
|
||||
fun displayName(): MutableComponent {
|
||||
return lang("ui.moon_phase.${this.name.lowercase()}")
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -71,7 +71,7 @@ class FishingSpawnCause(
|
||||
}
|
||||
|
||||
fun alterSize(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
|
||||
pokemonEntity.pokemon.scaleModifier *= effect.value.toFloat() / 1000.0f
|
||||
pokemonEntity.pokemon.scaleModifier += effect.value.toFloat() / 1000.0f
|
||||
}
|
||||
|
||||
fun saveDropsReroll(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
|
||||
|
||||
@@ -17,10 +17,10 @@ import com.cobblemon.mod.common.api.storage.factory.PokemonStoreFactory
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.google.gson.JsonObject
|
||||
import java.util.UUID
|
||||
import net.minecraft.core.RegistryAccess
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.server.level.ServerPlayer
|
||||
import java.util.UUID
|
||||
|
||||
/**
|
||||
* The base class for all stores of [Pokemon]. A store specifies the kind of coordinate it needs to be given
|
||||
@@ -77,7 +77,6 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
remove(pokemon)
|
||||
val position = getFirstAvailablePosition() ?: return false // Couldn't fit, shrug emoji
|
||||
set(position, pokemon)
|
||||
Cobblemon.playerDataManager.getTMData(uuid)?.syncTMsFromPokemon(pokemon)
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.decor
|
||||
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.InteractionResult
|
||||
import net.minecraft.world.entity.player.Player
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.block.state.properties.BooleanProperty
|
||||
import net.minecraft.world.level.gameevent.GameEvent
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class ActivatableDecorationBlock(settings: Properties, val type: ActivatableDecorationBlockType) : HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
|
||||
enum class ActivatableDecorationBlockType: StringRepresentable {
|
||||
//For basic use, also ignore camelcase errors, it's just dumb
|
||||
CENTERED_8x,
|
||||
//For specific items
|
||||
METRONOME;
|
||||
|
||||
override fun getSerializedName(): String = this.name.lowercase()
|
||||
|
||||
companion object {
|
||||
val CODEC = StringRepresentable.fromEnum(::values)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<ActivatableDecorationBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
ActivatableDecorationBlockType.CODEC.fieldOf("type").forGetter(ActivatableDecorationBlock::type)
|
||||
).apply(it, ::ActivatableDecorationBlock) }
|
||||
val ACTIVE = BooleanProperty.create("active")
|
||||
|
||||
val CENTERED_8x = Shapes.box(0.25, 0.0, 0.25, 0.75, 0.5, 0.75)
|
||||
val METRONOME_HITBOX = Shapes.box(0.25, 0.0, 0.375, 0.75, 0.8125, 0.75)
|
||||
|
||||
fun getShape(type: ActivatableDecorationBlockType): VoxelShape {
|
||||
return when (type) {
|
||||
ActivatableDecorationBlockType.METRONOME -> METRONOME_HITBOX
|
||||
else -> CENTERED_8x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HORIZONTAL_FACING, Direction.NORTH)
|
||||
.setValue(ACTIVE, false)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<ActivatableDecorationBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(HORIZONTAL_FACING, ACTIVE, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(HORIZONTAL_FACING)
|
||||
return rotateShape(Direction.NORTH, direction, getShape(type))
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return getCollisionShape(state, level, pos, context).singleEncompassing()
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
return super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
|
||||
}
|
||||
|
||||
override fun useWithoutItem(
|
||||
state: BlockState,
|
||||
level: Level,
|
||||
pos: BlockPos,
|
||||
player: Player,
|
||||
hitResult: BlockHitResult
|
||||
): InteractionResult {
|
||||
if (level.isClientSide) {
|
||||
level.setBlockAndUpdate(pos, state.cycle(ACTIVE))
|
||||
return InteractionResult.SUCCESS
|
||||
} else {
|
||||
this.push(state, level, pos, null as Player?)
|
||||
return InteractionResult.CONSUME
|
||||
}
|
||||
}
|
||||
|
||||
fun push(state: BlockState, level: Level, pos: BlockPos?, player: Player?) {
|
||||
var state = state
|
||||
state = state.cycle(ACTIVE)
|
||||
level.setBlock(pos!!, state, 3)
|
||||
level.gameEvent(player, if (state.getValue(ACTIVE)) GameEvent.BLOCK_ACTIVATE else GameEvent.BLOCK_DEACTIVATE, pos)
|
||||
}
|
||||
|
||||
override fun hasAnalogOutputSignal(state: BlockState): Boolean {
|
||||
return true
|
||||
}
|
||||
//TODO: Replace this forced 4 with a value that can be set by the block, only relevant when more blocks of this class are added.
|
||||
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
|
||||
return if (level.getBlockState(pos).getValue(ACTIVE) as Boolean) 4 else 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.util.StringRepresentable
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.HORIZONTAL_FACING
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class DecorativeItemBlock(settings: Properties, val type: DecorativeItemBlockType): HorizontalDirectionalBlock(settings), SimpleWaterloggedBlock {
|
||||
|
||||
enum class DecorativeItemBlockType: StringRepresentable {
|
||||
//For basic use, also ignore camelcase errors, it's just dumb
|
||||
CENTERED_8x,
|
||||
CENTERED_6x,
|
||||
FLATTENED_8x6,
|
||||
FLATTENED_8x4,
|
||||
FLATTENED_10x4,
|
||||
//For specific items
|
||||
TEACUP,
|
||||
TEAPOT,
|
||||
KNOT,
|
||||
POUCH,
|
||||
NEVERMELT,
|
||||
SCOPE,
|
||||
WIDE,
|
||||
ZOOM;
|
||||
|
||||
override fun getSerializedName(): String = this.name.lowercase()
|
||||
|
||||
companion object {
|
||||
val CODEC = StringRepresentable.fromEnum(::values)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<DecorativeItemBlock> = RecordCodecBuilder.mapCodec { it.group(
|
||||
propertiesCodec(),
|
||||
DecorativeItemBlockType.CODEC.fieldOf("type").forGetter(DecorativeItemBlock::type)
|
||||
).apply(it, ::DecorativeItemBlock) }
|
||||
|
||||
val CENTERED_8x = Shapes.box(0.25, 0.0, 0.25, 0.75, 0.5, 0.75)
|
||||
val CENTERED_6x = Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.375, 0.6875)
|
||||
val FLAT6_HITBOX = Shapes.box(0.25, 0.0, 0.25, 0.75, 0.375, 0.75)
|
||||
val FLAT4_HITBOX = Shapes.box(0.25, 0.0, 0.25, 0.75, 0.25, 0.75)
|
||||
val SMOOSHED4_HITBOX = Shapes.box(0.1875, 0.0, 0.1875, 0.8125, 0.25, 0.8125)
|
||||
val TEACUP_HITBOX = Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.25, 0.6875)
|
||||
val TEAPOT_HITBOX = Shapes.box(0.1875, 0.0, 0.1875, 0.8125, 0.625, 0.8125)
|
||||
val KNOT_HITBOX = Shapes.box(0.375, 0.0, 0.375, 0.75, 0.375, 0.75)
|
||||
val POUCH_HITBOX = Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.3125, 0.6875)
|
||||
val NEVERMELT_HITBOX = Shapes.box(0.25, 0.0, 0.3125, 0.6875, 0.25, 0.75)
|
||||
val SCOPE_HITBOX = Shapes.box(0.625, 0.0, 0.4375, 0.9375, 0.125, 0.75)
|
||||
val WIDE_HITBOX = Shapes.box(0.625, 0.0, 0.125, 0.9375, 0.0625, 0.625)
|
||||
val ZOOM_HITBOX = Shapes.box(0.125, 0.0, 0.1875, 0.375, 0.1875, 0.5)
|
||||
|
||||
fun getShape(type: DecorativeItemBlockType): VoxelShape {
|
||||
return when (type) {
|
||||
DecorativeItemBlockType.TEAPOT -> TEAPOT_HITBOX
|
||||
DecorativeItemBlockType.TEACUP -> TEACUP_HITBOX
|
||||
DecorativeItemBlockType.KNOT -> KNOT_HITBOX
|
||||
DecorativeItemBlockType.POUCH -> POUCH_HITBOX
|
||||
DecorativeItemBlockType.NEVERMELT -> NEVERMELT_HITBOX
|
||||
DecorativeItemBlockType.SCOPE -> SCOPE_HITBOX
|
||||
DecorativeItemBlockType.WIDE -> WIDE_HITBOX
|
||||
DecorativeItemBlockType.ZOOM -> ZOOM_HITBOX
|
||||
DecorativeItemBlockType.FLATTENED_10x4 -> SMOOSHED4_HITBOX
|
||||
DecorativeItemBlockType.FLATTENED_8x4 -> FLAT4_HITBOX
|
||||
DecorativeItemBlockType.FLATTENED_8x6 -> FLAT6_HITBOX
|
||||
DecorativeItemBlockType.CENTERED_6x -> CENTERED_6x
|
||||
else -> CENTERED_8x
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(HORIZONTAL_FACING, Direction.NORTH)
|
||||
.setValue(WATERLOGGED, false))
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<DecorativeItemBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(HORIZONTAL_FACING, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? = if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (state.getValue(HORIZONTAL_FACING).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(HORIZONTAL_FACING)
|
||||
return rotateShape(Direction.NORTH, direction, getShape(type))
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
return super.getStateForPlacement(context)?.setValue(HORIZONTAL_FACING, context.horizontalDirection.opposite)?.setValue(WATERLOGGED, fluidState.type === Fluids.WATER)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.util.rotateShape
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.ButtonBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockSetType
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class EjectButtonBlock(properties: Properties, ticksToStayPressed: Int) : ButtonBlock(BlockSetType.IRON, ticksToStayPressed, properties), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val FLOOR_UNPOWERED = Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.1875, 0.875),
|
||||
Shapes.box(0.375, 0.1875, 0.375, 0.625, 0.3125, 0.625) )
|
||||
val FLOOR_POWERED = Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.1875, 0.875),
|
||||
Shapes.box(0.375, 0.1875, 0.375, 0.625, 0.25, 0.625) )
|
||||
val FLOOR_PANEL = Shapes.box(0.125, 0.0, 0.125, 0.875, 0.1875, 0.875)
|
||||
val CEILING_UNPOWERED = Shapes.or(
|
||||
Shapes.box(0.125, 0.8125, 0.125, 0.875, 1.0, 0.875),
|
||||
Shapes.box(0.375, 0.6875, 0.375, 0.625, 0.8125, 0.625) )
|
||||
val CEILING_POWERED = Shapes.or(
|
||||
Shapes.box(0.125, 0.8125, 0.125, 0.875, 1.0, 0.875),
|
||||
Shapes.box(0.375, 0.75, 0.375, 0.625, 0.8125, 0.625) )
|
||||
val CEILING_PANEL = Shapes.box(0.125, 0.8125, 0.125, 0.875, 1.0, 0.875)
|
||||
val WALL_UNPOWERED = Shapes.or(
|
||||
Shapes.box(0.125, 0.125, 0.8125, 0.875, 0.875, 1.0),
|
||||
Shapes.box(0.375, 0.375, 0.6875, 0.625, 0.625, 0.8125) )
|
||||
val WALL_POWERED = Shapes.or(
|
||||
Shapes.box(0.125, 0.125, 0.8125, 0.875, 0.875, 1.0),
|
||||
Shapes.box(0.375, 0.375, 0.75, 0.625, 0.625, 0.8125) )
|
||||
val WALL_PANEL = Shapes.box(0.125, 0.125, 0.8125, 0.875, 0.875, 1.0)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(FACE, AttachFace.WALL)
|
||||
.setValue(POWERED, false)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, FACE, POWERED, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? {
|
||||
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState);
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
for (direction in context.nearestLookingDirections) {
|
||||
val blockState: BlockState
|
||||
if (direction.getAxis() === Direction.Axis.Y) {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, if (direction == Direction.UP) AttachFace.CEILING else AttachFace.FLOOR) as BlockState)
|
||||
.setValue(FACING, context.horizontalDirection.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
} else {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, AttachFace.WALL) as BlockState)
|
||||
.setValue(FACING, direction.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
}
|
||||
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return when (state.getValue(FACE)) {
|
||||
AttachFace.FLOOR -> if (state.getValue(POWERED)) {FLOOR_POWERED} else {FLOOR_UNPOWERED}
|
||||
AttachFace.WALL ->
|
||||
when (direction) {
|
||||
Direction.NORTH, Direction.UP, Direction.DOWN -> if (state.getValue(POWERED)) {WALL_POWERED} else {WALL_UNPOWERED}
|
||||
else -> rotateShape(Direction.NORTH, direction, if (state.getValue(POWERED)) {WALL_POWERED} else {WALL_UNPOWERED})
|
||||
}
|
||||
AttachFace.CEILING -> if (state.getValue(POWERED)) {CEILING_POWERED} else {CEILING_UNPOWERED}
|
||||
}
|
||||
}
|
||||
override fun getCollisionShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return when (state.getValue(FACE)) {
|
||||
AttachFace.FLOOR -> FLOOR_PANEL
|
||||
AttachFace.WALL -> when (direction) {
|
||||
Direction.NORTH, Direction.UP, Direction.DOWN -> WALL_PANEL
|
||||
else -> rotateShape(Direction.NORTH, direction, WALL_PANEL)
|
||||
}
|
||||
AttachFace.CEILING -> CEILING_PANEL
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (getConnectedDirection(state).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return if (state.getValue(FACE) != AttachFace.WALL) {
|
||||
val direction = getConnectedDirection(state)
|
||||
canSupportCenter(level, pos.relative(direction.opposite), direction)
|
||||
} else {
|
||||
level.getBlockState(pos.relative(state.getValue(FACING).opposite)).isSolid
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class GalaricaWreathBlock(properties: Properties) : SixFaceRotationalBlock(properties) {
|
||||
companion object {
|
||||
val CODEC: MapCodec<GalaricaWreathBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(propertiesCodec()).apply(it, ::GalaricaWreathBlock)
|
||||
}
|
||||
|
||||
val FLOOR_NORTH = Shapes.box(0.125, 0.0, 0.125, 0.875, 0.25, 0.875)
|
||||
val WALL_NORTH = Shapes.box(0.125, 0.125, 0.75, 0.875, 0.875, 1.0)
|
||||
val CEILING_NORTH = Shapes.box(0.125, 0.125, 0.375, 0.875, 0.625, 0.5625)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(FACE, AttachFace.WALL)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<GalaricaWreathBlock> = CODEC
|
||||
|
||||
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return when (state.getValue(FACE)) {
|
||||
AttachFace.FLOOR ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, FLOOR_NORTH)
|
||||
} else FLOOR_NORTH
|
||||
AttachFace.WALL ->
|
||||
when (direction) {
|
||||
Direction.NORTH, Direction.UP, Direction.DOWN -> WALL_NORTH
|
||||
else -> rotateShape(Direction.NORTH, direction, WALL_NORTH)
|
||||
}
|
||||
AttachFace.CEILING ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, CEILING_NORTH)
|
||||
} else CEILING_NORTH
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class MagnetBlock(properties: Properties) : SixFaceRotationalBlock(properties) {
|
||||
companion object {
|
||||
val CODEC: MapCodec<MagnetBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(propertiesCodec()).apply(it, ::MagnetBlock)
|
||||
}
|
||||
|
||||
val FLOOR_NORTH = Shapes.box(0.25, 0.0, 0.25, 0.75, 0.1875, 0.75)
|
||||
val WALL_NORTH = Shapes.box(0.25, 0.25, 0.8125, 0.75, 0.75, 1.0)
|
||||
val CEILING_NORTH = Shapes.box(0.25, 0.8125, 0.25, 0.75, 1.0, 0.75)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(FACE, AttachFace.WALL)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<MagnetBlock> = CODEC
|
||||
|
||||
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return when (state.getValue(FACE)) {
|
||||
AttachFace.FLOOR ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, FLOOR_NORTH)
|
||||
} else FLOOR_NORTH
|
||||
AttachFace.WALL ->
|
||||
when (direction) {
|
||||
Direction.NORTH, Direction.UP, Direction.DOWN -> WALL_NORTH
|
||||
else -> rotateShape(Direction.NORTH, direction, WALL_NORTH)
|
||||
}
|
||||
AttachFace.CEILING ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, CEILING_NORTH)
|
||||
} else CEILING_NORTH
|
||||
}
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.util.rotateShape
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class OrbBlock(properties: Properties) : SixFaceRotationalBlock(properties) {
|
||||
companion object {
|
||||
val CODEC: MapCodec<OrbBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(propertiesCodec()).apply(it, ::OrbBlock)
|
||||
}
|
||||
|
||||
val FLOOR_NORTH = Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.375, 0.6875)
|
||||
val WALL_NORTH = Shapes.box(0.3125, 0.3125, 0.625, 0.6875, 0.6875, 1.0)
|
||||
val CEILING_NORTH = Shapes.box(0.3125, 0.625, 0.375, 0.6875, 1.0, 0.6875)
|
||||
}
|
||||
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(FACE, AttachFace.WALL)
|
||||
.setValue(WATERLOGGED, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<OrbBlock> = CODEC
|
||||
|
||||
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
|
||||
val direction = state.getValue(FACING)
|
||||
return when (state.getValue(FACE)) {
|
||||
AttachFace.FLOOR ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, FLOOR_NORTH)
|
||||
} else FLOOR_NORTH
|
||||
AttachFace.WALL ->
|
||||
when (direction) {
|
||||
Direction.NORTH, Direction.UP, Direction.DOWN -> WALL_NORTH
|
||||
else -> rotateShape(Direction.NORTH, direction, WALL_NORTH)
|
||||
}
|
||||
AttachFace.CEILING ->
|
||||
if (direction.axis === Direction.Axis.X) {
|
||||
rotateShape(Direction.NORTH, Direction.WEST, CEILING_NORTH)
|
||||
} else CEILING_NORTH
|
||||
}
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -13,25 +13,15 @@ import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.FaceAttachedHorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class PlaqueBlock(properties : Properties) : FaceAttachedHorizontalDirectionalBlock(properties), SimpleWaterloggedBlock {
|
||||
class PlaqueBlock(properties : Properties) : SixFaceRotationalBlock(properties) {
|
||||
companion object {
|
||||
val CODEC: MapCodec<PlaqueBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(propertiesCodec()).apply(it, ::PlaqueBlock)
|
||||
@@ -54,40 +44,6 @@ class PlaqueBlock(properties : Properties) : FaceAttachedHorizontalDirectionalBl
|
||||
)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, FACE, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? {
|
||||
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState);
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
for (direction in context.nearestLookingDirections) {
|
||||
val blockState: BlockState
|
||||
if (direction.getAxis() === Direction.Axis.Y) {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, if (direction == Direction.UP) AttachFace.CEILING else AttachFace.FLOOR) as BlockState)
|
||||
.setValue(FACING, context.horizontalDirection.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
} else {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, AttachFace.WALL) as BlockState)
|
||||
.setValue(FACING, direction.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
|
||||
}
|
||||
|
||||
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<PlaqueBlock> = CODEC
|
||||
|
||||
override fun getShape(state: BlockState, blockGetter: BlockGetter, pos: BlockPos, collisionContext: CollisionContext): VoxelShape {
|
||||
@@ -108,21 +64,4 @@ class PlaqueBlock(properties : Properties) : FaceAttachedHorizontalDirectionalBl
|
||||
} else CEILING_NORTH
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (getConnectedDirection(state).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return if (state.getValue(FACE) == AttachFace.CEILING) {
|
||||
val direction = getConnectedDirection(state)
|
||||
canSupportCenter(level, pos.relative(direction.opposite), direction)
|
||||
} else {
|
||||
super.canSurvive(state, level, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
/*
|
||||
* 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.decor
|
||||
|
||||
import com.mojang.serialization.MapCodec
|
||||
import com.mojang.serialization.codecs.RecordCodecBuilder
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.BlockGetter
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.*
|
||||
import net.minecraft.world.level.block.HorizontalDirectionalBlock.FACING
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.block.state.properties.Property
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
import net.minecraft.world.phys.shapes.CollisionContext
|
||||
import net.minecraft.world.phys.shapes.Shapes
|
||||
import net.minecraft.world.phys.shapes.VoxelShape
|
||||
|
||||
class RingTargetBlock(settings: Properties) : TargetBlock(settings), SimpleWaterloggedBlock {
|
||||
init {
|
||||
registerDefaultState(stateDefinition.any()
|
||||
.setValue(FACING, Direction.NORTH)
|
||||
.setValue(WATERLOGGED, false)
|
||||
.setValue(OUTPUT_POWER, 0))
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(ctx: BlockPlaceContext): BlockState? {
|
||||
var blockState = defaultBlockState()
|
||||
.setValue(WATERLOGGED, ctx.level.getFluidState(ctx.clickedPos).type == Fluids.WATER)
|
||||
val worldView = ctx.level
|
||||
val blockPos = ctx.clickedPos
|
||||
ctx.nearestLookingDirections.forEach { direction ->
|
||||
if (direction.axis.isHorizontal) {
|
||||
blockState = blockState.setValue(FACING, direction.opposite) as BlockState
|
||||
if (blockState.canSurvive(worldView, blockPos)) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? {
|
||||
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState)
|
||||
}
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING)
|
||||
builder.add(WATERLOGGED)
|
||||
builder.add(*arrayOf<Property<*>>(OUTPUT_POWER))
|
||||
}
|
||||
|
||||
override fun getShape(
|
||||
state: BlockState,
|
||||
blockGetter: BlockGetter,
|
||||
pos: BlockPos,
|
||||
collisionContext: CollisionContext
|
||||
): VoxelShape {
|
||||
return when (state.getValue(FACING)) {
|
||||
Direction.SOUTH -> FULL_PLANE_SOUTH
|
||||
Direction.WEST -> FULL_PLANE_WEST
|
||||
Direction.EAST -> FULL_PLANE_EAST
|
||||
else -> FULL_PLANE_NORTH
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateShape(
|
||||
state: BlockState,
|
||||
direction: Direction,
|
||||
neighborState: BlockState,
|
||||
world: LevelAccessor,
|
||||
pos: BlockPos,
|
||||
neighborPos: BlockPos
|
||||
): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (direction.opposite == state.getValue(FACING) && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun rotate(state: BlockState, rotation: Rotation): BlockState {
|
||||
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)))
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return level.getBlockState(pos.relative(state.getValue(FACING).opposite)).isSolid
|
||||
}
|
||||
|
||||
companion object {
|
||||
val CODEC: MapCodec<RingTargetBlock> = RecordCodecBuilder.mapCodec {
|
||||
it.group(
|
||||
propertiesCodec()
|
||||
).apply(it, ::RingTargetBlock) }
|
||||
|
||||
val OUTPUT_POWER = BlockStateProperties.POWER;
|
||||
|
||||
val FULL_PLANE_NORTH = Shapes.box(0.0, 0.0, 0.925, 1.0, 1.0, 1.0)
|
||||
val FULL_PLANE_WEST = Shapes.box(0.925, 0.0, 0.0, 1.0, 1.0, 1.0)
|
||||
val FULL_PLANE_SOUTH = Shapes.box(0.0, 0.0, 0.0, 1.0, 1.0, 0.075)
|
||||
val FULL_PLANE_EAST = Shapes.box(0.0, 0.0, 0.0, 0.075, 1.0, 1.0)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.mojang.serialization.MapCodec
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.core.Direction
|
||||
import net.minecraft.world.item.context.BlockPlaceContext
|
||||
import net.minecraft.world.level.LevelAccessor
|
||||
import net.minecraft.world.level.LevelReader
|
||||
import net.minecraft.world.level.block.Block
|
||||
import net.minecraft.world.level.block.Blocks
|
||||
import net.minecraft.world.level.block.FaceAttachedHorizontalDirectionalBlock
|
||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock
|
||||
import net.minecraft.world.level.block.state.BlockState
|
||||
import net.minecraft.world.level.block.state.StateDefinition
|
||||
import net.minecraft.world.level.block.state.properties.AttachFace
|
||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED
|
||||
import net.minecraft.world.level.material.FluidState
|
||||
import net.minecraft.world.level.material.Fluids
|
||||
|
||||
open class SixFaceRotationalBlock(properties : Properties) : FaceAttachedHorizontalDirectionalBlock(properties), SimpleWaterloggedBlock {
|
||||
companion object {
|
||||
val CODEC = simpleCodec(::SixFaceRotationalBlock)
|
||||
}
|
||||
|
||||
override fun codec(): MapCodec<out SixFaceRotationalBlock> = CODEC
|
||||
|
||||
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
|
||||
builder.add(FACING, FACE, WATERLOGGED)
|
||||
}
|
||||
|
||||
override fun getFluidState(blockState: BlockState): FluidState? {
|
||||
return if (blockState.getValue(WATERLOGGED)) Fluids.WATER.getSource(false) else super.getFluidState(blockState);
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
val fluidState = context.level.getFluidState(context.clickedPos)
|
||||
for (direction in context.nearestLookingDirections) {
|
||||
val blockState: BlockState
|
||||
if (direction.getAxis() === Direction.Axis.Y) {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, if (direction == Direction.UP) AttachFace.CEILING else AttachFace.FLOOR) as BlockState)
|
||||
.setValue(FACING, context.horizontalDirection.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
} else {
|
||||
blockState = (defaultBlockState()
|
||||
.setValue(FACE, AttachFace.WALL) as BlockState)
|
||||
.setValue(FACING, direction.opposite)
|
||||
.setValue(WATERLOGGED, fluidState.type === Fluids.WATER
|
||||
) as BlockState
|
||||
|
||||
}
|
||||
|
||||
if (blockState.canSurvive(context.getLevel(), context.getClickedPos())) {
|
||||
return blockState
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun updateShape(state: BlockState, direction: Direction, neighborState: BlockState, world: LevelAccessor, pos: BlockPos, neighborPos: BlockPos): BlockState {
|
||||
if (state.getValue(WATERLOGGED)) {
|
||||
world.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world))
|
||||
}
|
||||
return if (getConnectedDirection(state).opposite == direction && !state.canSurvive(world, pos)) Blocks.AIR.defaultBlockState()
|
||||
else super.updateShape(state, direction, neighborState, world, pos, neighborPos)
|
||||
}
|
||||
|
||||
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
|
||||
return if (state.getValue(FACE) == AttachFace.CEILING) {
|
||||
val direction = getConnectedDirection(state)
|
||||
canSupportCenter(level, pos.relative(direction.opposite), direction)
|
||||
} else if (state.getValue(FACE) == AttachFace.WALL) {
|
||||
level.getBlockState(pos.relative(state.getValue(FACING).opposite)).isSolid
|
||||
} else {
|
||||
super.canSurvive(state, level, pos)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,7 +39,13 @@ class StackableItemBlock(settings: Properties, val type: StackableItemBlockType)
|
||||
ETHER,
|
||||
MAX_POTION,
|
||||
POTION,
|
||||
STATUS_RESTORE;
|
||||
STATUS_RESTORE,
|
||||
VITAMIN,
|
||||
METAL_COAT,
|
||||
IRON_BALL,
|
||||
THROAT_SPRAY,
|
||||
UPGRADE,
|
||||
DICE;
|
||||
|
||||
override fun getSerializedName(): String = this.name.lowercase()
|
||||
|
||||
@@ -57,98 +63,78 @@ class StackableItemBlock(settings: Properties, val type: StackableItemBlockType)
|
||||
StackableItemBlockType.CODEC.fieldOf("type").forGetter(StackableItemBlock::type)
|
||||
).apply(it, ::StackableItemBlock) }
|
||||
|
||||
val northShapeMap: Map<StackableItemBlockType, List<VoxelShape>> = mapOf(
|
||||
StackableItemBlockType.BATTLE_ITEM to listOf(
|
||||
Shapes.box(0.34375, 0.0, 0.34375, 0.65625, 0.4375, 0.65625),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.34375, 0.40625, 0.4375, 0.65625),
|
||||
Shapes.box(0.59375, 0.0, 0.40625, 0.90625, 0.4375, 0.71875)
|
||||
val northShapeMap: Map<StackableItemBlockType, List<VoxelShape>>
|
||||
get() = mapOf(
|
||||
StackableItemBlockType.BATTLE_ITEM to listOf(
|
||||
Shapes.box(0.34375, 0.0, 0.34375, 0.65625, 0.4375, 0.65625),
|
||||
Shapes.box(0.09375, 0.0, 0.34375, 0.90625, 0.4375, 0.71875),
|
||||
Shapes.box(0.09375, 0.0, 0.09375, 0.90625, 0.4375, 0.90625),
|
||||
Shapes.box(0.09375, 0.0, 0.09375, 0.96875, 0.4375, 0.90625)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.59375, 0.0, 0.59375, 0.90625, 0.4375, 0.90625),
|
||||
Shapes.box(0.09375, 0.0, 0.53125, 0.40625, 0.4375, 0.84375),
|
||||
Shapes.box(0.34375, 0.0, 0.09375, 0.65625, 0.4375, 0.40625)
|
||||
StackableItemBlockType.ETHER to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5, 0.6875),
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.875, 0.5, 0.75),
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.5, 1.0),
|
||||
Shapes.box(0.125, 0.0, 0.0625, 0.9375, 0.5, 1.0)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.09375, 0.40625, 0.4375, 0.40625),
|
||||
Shapes.box(0.59375, 0.0, 0.15625, 0.90625, 0.4375, 0.46875),
|
||||
Shapes.box(0.65625, 0.0, 0.59375, 0.96875, 0.4375, 0.90625),
|
||||
Shapes.box(0.15625, 0.0, 0.53125, 0.46875, 0.4375, 0.84375)
|
||||
)
|
||||
),
|
||||
StackableItemBlockType.ETHER to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5, 0.6875),
|
||||
Shapes.or(
|
||||
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.5, 0.75),
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.5, 0.6875)
|
||||
StackableItemBlockType.MAX_POTION to listOf(
|
||||
Shapes.box(0.34375, 0.0, 0.3125, 0.65625, 0.5, 0.6875),
|
||||
Shapes.box(0.09375, 0.0, 0.3125, 0.90625, 0.5, 0.75),
|
||||
Shapes.box(0.09375, 0.0, 0.0625, 0.90625, 0.5, 1.0),
|
||||
Shapes.box(0.09375, 0.0, 0.0625, 0.96875, 0.5, 1.0)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.375, 0.0, 0.125, 0.625, 0.5, 0.5),
|
||||
Shapes.box(0.125, 0.0, 0.5625, 0.375, 0.5, 0.9375),
|
||||
Shapes.box(0.625, 0.0, 0.625, 0.875, 0.5, 1.0)
|
||||
StackableItemBlockType.POTION to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5625, 0.75),
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.875, 0.5625, 0.8125),
|
||||
Shapes.box(0.125, 0.0, 0.0625, 0.875, 0.5625, 1.0),
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.9375, 0.5625, 1.0)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.0625, 0.375, 0.5, 0.4375),
|
||||
Shapes.box(0.625, 0.0, 0.125, 0.875, 0.5, 0.5),
|
||||
Shapes.box(0.6875, 0.0, 0.625, 0.9375, 0.5, 1.0),
|
||||
Shapes.box(0.1875, 0.0, 0.5625, 0.4375, 0.5, 0.9375)
|
||||
)
|
||||
),
|
||||
StackableItemBlockType.MAX_POTION to listOf(
|
||||
Shapes.box(0.34375, 0.0, 0.3125, 0.65625, 0.5, 0.6875),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.3125, 0.40625, 0.5, 0.6875),
|
||||
Shapes.box(0.59375, 0.0, 0.375, 0.90625, 0.5, 0.75)
|
||||
StackableItemBlockType.STATUS_RESTORE to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.4375, 0.6875),
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.875, 0.4375, 0.75),
|
||||
Shapes.box(0.125, 0.0, 0.0625, 0.875, 0.4375, 0.9375),
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.9375, 0.4375, 0.9375)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.34375, 0.0, 0.0625, 0.65625, 0.5, 0.4375),
|
||||
Shapes.box(0.09375, 0.0, 0.5625, 0.40625, 0.5, 0.9375),
|
||||
Shapes.box(0.59375, 0.0, 0.625, 0.90625, 0.5, 1.0)
|
||||
StackableItemBlockType.METAL_COAT to listOf(
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.375, 0.6875),
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.375, 0.875),
|
||||
Shapes.box(0.125, 0.0, 0.0625, 0.9375, 0.375, 0.875),
|
||||
Shapes.box(0.0625, 0.0, 0.0625, 0.9375, 0.375, 0.9375)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.09375, 0.0, 0.0625, 0.40625, 0.5, 0.4375),
|
||||
Shapes.box(0.15625, 0.0, 0.5625, 0.46875, 0.5, 0.9375),
|
||||
Shapes.box(0.65625, 0.0, 0.625, 0.96875, 0.5, 1.0),
|
||||
Shapes.box(0.59375, 0.0, 0.125, 0.90625, 0.5, 0.5)
|
||||
).singleEncompassing()
|
||||
),
|
||||
StackableItemBlockType.POTION to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.5625, 0.75),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.5625, 0.75),
|
||||
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.5625, 0.8125)
|
||||
StackableItemBlockType.VITAMIN to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.375, 0.625, 0.4375, 0.625),
|
||||
Shapes.box(0.1875, 0.0, 0.3125, 0.8125, 0.4375, 0.6875),
|
||||
Shapes.box(0.1875, 0.0, 0.1875, 0.8125, 0.4375, 0.8125),
|
||||
Shapes.box(0.125, 0.0, 0.125, 0.875, 0.4375, 0.875)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.375, 0.0, 0.0625, 0.625, 0.5625, 0.5),
|
||||
Shapes.box(0.625, 0.0, 0.5625, 0.875, 0.5625, 1.0),
|
||||
Shapes.box(0.125, 0.0, 0.5, 0.375, 0.5625, 0.9375)
|
||||
StackableItemBlockType.IRON_BALL to listOf(
|
||||
Shapes.box(0.25, 0.0, 0.25, 0.75, 0.4375, 0.75),
|
||||
Shapes.box(0.0, 0.0, 0.1875, 1.0, 0.4375, 0.8125),
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.4375, 1.0),
|
||||
Shapes.or(
|
||||
Shapes.box(0.0, 0.0, 0.0, 1.0, 0.4375, 1.0),
|
||||
Shapes.box(0.25, 0.4375, 0.3125, 0.6875, 0.875, 0.75)
|
||||
)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.375, 0.5625, 0.4375),
|
||||
Shapes.box(0.625, 0.0, 0.0625, 0.875, 0.5625, 0.5),
|
||||
Shapes.box(0.6875, 0.0, 0.5625, 0.9375, 0.5625, 1.0),
|
||||
Shapes.box(0.1875, 0.0, 0.5, 0.4375, 0.5625, 0.9375)
|
||||
)
|
||||
),
|
||||
StackableItemBlockType.STATUS_RESTORE to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.3125, 0.625, 0.4375, 0.6875),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.3125, 0.375, 0.4375, 0.75),
|
||||
Shapes.box(0.625, 0.0, 0.375, 0.875, 0.4375, 0.75)
|
||||
StackableItemBlockType.THROAT_SPRAY to listOf(
|
||||
Shapes.box(0.375, 0.0, 0.1875, 0.625, 0.5625, 0.75),
|
||||
Shapes.box(0.125, 0.0, 0.1875, 0.875, 0.5625, 0.8125),
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.875, 0.5625, 1.0),
|
||||
Shapes.box(0.075, 0.0, 0.0, 0.9375, 0.5625, 1.0)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.125, 0.0, 0.5, 0.375, 0.4375, 0.875),
|
||||
Shapes.box(0.625, 0.0, 0.5625, 0.875, 0.4375, 0.9375),
|
||||
Shapes.box(0.375, 0.0, 0.0625, 0.625, 0.4375, 0.4375)
|
||||
StackableItemBlockType.UPGRADE to listOf(
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.125, 0.6875),
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.25, 0.6875),
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.375, 0.6875),
|
||||
Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.5, 0.6875)
|
||||
),
|
||||
Shapes.or(
|
||||
Shapes.box(0.1875, 0.0, 0.5, 0.4375, 0.4375, 0.875),
|
||||
Shapes.box(0.125, 0.0, 0.0, 0.375, 0.4375, 0.375),
|
||||
Shapes.box(0.625, 0.0, 0.0625, 0.875, 0.4375, 0.4375),
|
||||
Shapes.box(0.6875, 0.0, 0.5625, 0.9375, 0.4375, 0.9375)
|
||||
StackableItemBlockType.DICE to listOf(
|
||||
Shapes.box(0.125, 0.0, 0.6875, 0.3125, 0.1875, 0.875),
|
||||
Shapes.box(0.125, 0.0, 0.625, 0.5625, 0.1875, 0.875),
|
||||
Shapes.box(0.125, 0.0, 0.4375, 0.5625, 0.1875, 0.875),
|
||||
Shapes.box(0.125, 0.0, 0.4375, 0.5625, 0.375, 0.875)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
fun getShape(type: StackableItemBlockType, count: Int): VoxelShape = northShapeMap[type]?.let { list -> list[(count - 1).coerceIn(0, list.lastIndex)] } ?: Shapes.block()
|
||||
fun getMaxAmount(type: StackableItemBlockType): Int = min(northShapeMap[type]?.size ?: 1 , MAX_AMOUNT)
|
||||
@@ -184,7 +170,7 @@ class StackableItemBlock(settings: Properties, val type: StackableItemBlockType)
|
||||
}
|
||||
|
||||
override fun getShape(state: BlockState, level: BlockGetter, pos: BlockPos, context: CollisionContext): VoxelShape {
|
||||
return getCollisionShape(state, level, pos, context).singleEncompassing()
|
||||
return getCollisionShape(state, level, pos, context)
|
||||
}
|
||||
|
||||
override fun getStateForPlacement(context: BlockPlaceContext): BlockState? {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
package com.cobblemon.mod.common.block
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonBlocks
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
|
||||
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.SHOULD_GROW
|
||||
@@ -33,6 +34,9 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
||||
const val MIN_DISTANCE_BETWEEN_GEMS = 1
|
||||
const val CONTINUATION_CHANCE = 0.8f
|
||||
|
||||
// Upper safety check to prevent infinite loops
|
||||
const val MAX_FORCE_GROW_ATTEMPTS = 64
|
||||
|
||||
val BLOCK_TO_CLUSTER: Map<ResourceLocation, Block> by lazy {
|
||||
mapOf(
|
||||
ResourceLocation.parse("cobblemon:normal_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
|
||||
@@ -112,13 +116,16 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
||||
fun forceGrow(level: WorldGenLevel, pos: BlockPos, random: RandomSource, percentage: Float) {
|
||||
val desiredLength = MAX_CONNECTED_GEMS * percentage
|
||||
|
||||
while (true) {
|
||||
val growInfo = grow(level, pos, random, true)
|
||||
val grown = growInfo.first
|
||||
val clusterSize = growInfo.second
|
||||
|
||||
if (!grown || clusterSize >= desiredLength) break
|
||||
repeat(MAX_FORCE_GROW_ATTEMPTS) {
|
||||
val (grown, clusterSize) = grow(level, pos, random, true)
|
||||
if (!grown || clusterSize >= desiredLength) return
|
||||
}
|
||||
|
||||
Cobblemon.LOGGER.warn(
|
||||
"Forced type gem growth at {} stopped after {} attempts without reaching its target size; keeping the partial formation.",
|
||||
pos,
|
||||
MAX_FORCE_GROW_ATTEMPTS
|
||||
)
|
||||
}
|
||||
|
||||
private fun grow(
|
||||
@@ -130,7 +137,7 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
||||
val connectedGems = getConnectedGemBlocks(level, pos) // maybe we can store this and access it for better performance? check for changes in list rather than checking each time grow is called? idk
|
||||
val gemCount = connectedGems.size
|
||||
val overLimit = gemCount >= MAX_CONNECTED_GEMS
|
||||
|
||||
|
||||
if (overLimit) {
|
||||
// if there is no open air pockets to grow any new clusters then exit early
|
||||
if (!hasBreathingRoom(level, connectedGems)) {
|
||||
@@ -165,6 +172,11 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
||||
|
||||
level.setBlock(targetPos, placeState, UPDATE_ALL)
|
||||
|
||||
// During world gen, the region silently discards writes into neighboring chunks that are already fully generated.
|
||||
// Through ImposterProtoChunk with writes set to disabled. Checks if the block is still air after the write and skips if so as it can't be filled.
|
||||
// Preventing infinite loops.
|
||||
if (!level.getBlockState(targetPos).`is`(clusterBlock)) continue
|
||||
|
||||
if (forced) {
|
||||
forceAdvanceClusterGrowth(level, targetPos, random)
|
||||
}
|
||||
@@ -210,4 +222,4 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
|
||||
}
|
||||
|
||||
override fun isRandomlyTicking(state: BlockState): Boolean = true
|
||||
}
|
||||
}
|
||||
@@ -128,6 +128,7 @@ class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobbl
|
||||
|
||||
fun isValidItem(stack: ItemStack): Boolean {
|
||||
return stack.item is TechnicalMachineItem
|
||||
|| stack.item == CobblemonItems.BLANK_TM
|
||||
|| stack.item == CobblemonItems.UPGRADE
|
||||
|| stack.item == CobblemonItems.DUBIOUS_DISC
|
||||
|| stack.has(DataComponents.JUKEBOX_PLAYABLE)
|
||||
|
||||
+2
@@ -31,6 +31,7 @@ import com.cobblemon.mod.common.client.sound.instances.CancellableSoundInstance
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.item.PokeBallItem
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.cobblemon.mod.common.util.giveOrDropItemStack
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
@@ -235,6 +236,7 @@ class FossilMultiblockStructure (
|
||||
entity.setPos(fixedPosition.center.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.addFreshEntity(entity)) {
|
||||
pokemon.state = SentOutState(entity)
|
||||
CobblemonEvents.FOSSIL_REVIVED.post(FossilRevivedEvent(pokemon, null))
|
||||
return true
|
||||
} else {
|
||||
|
||||
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.storage.player.client.ClientPokedexManager
|
||||
import com.cobblemon.mod.common.api.storage.player.client.ClientTMMoveManager
|
||||
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.block.entity.TintBlockEntity
|
||||
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
|
||||
import com.cobblemon.mod.common.client.battle.ClientBattle
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlay
|
||||
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
|
||||
@@ -44,6 +45,7 @@ import com.cobblemon.mod.common.client.render.entity.PokeBobberEntityRenderer
|
||||
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
|
||||
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
|
||||
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PlayerRecallBallRenderer
|
||||
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
|
||||
@@ -238,6 +240,12 @@ object CobblemonClient {
|
||||
CobblemonBlocks.POKE_CAKE
|
||||
)
|
||||
|
||||
this.implementation.registerBlockRenderType(
|
||||
RenderType.translucent(),
|
||||
CobblemonBlocks.UPGRADE,
|
||||
CobblemonBlocks.DUBIOUS_DISC
|
||||
)
|
||||
|
||||
this.implementation.registerBlockRenderType(
|
||||
RenderType.cutout(),
|
||||
CobblemonBlocks.GILDED_CHEST,
|
||||
@@ -383,7 +391,31 @@ object CobblemonClient {
|
||||
CobblemonBlocks.X_SP_ATK,
|
||||
CobblemonBlocks.X_SP_DEF,
|
||||
CobblemonBlocks.X_SPEED,
|
||||
CobblemonBlocks.TM_MACHINE
|
||||
CobblemonBlocks.TM_MACHINE,
|
||||
|
||||
CobblemonBlocks.GALARICA_CUFF,
|
||||
CobblemonBlocks.GALARICA_WREATH,
|
||||
CobblemonBlocks.CHIPPED_POT,
|
||||
CobblemonBlocks.CRACKED_POT,
|
||||
CobblemonBlocks.UNREMARKABLE_TEACUP,
|
||||
CobblemonBlocks.MASTERPIECE_TEACUP,
|
||||
CobblemonBlocks.LUMINOUS_MOSS,
|
||||
CobblemonBlocks.ICY_ROCK,
|
||||
CobblemonBlocks.HEAT_ROCK,
|
||||
CobblemonBlocks.SMOOTH_ROCK,
|
||||
CobblemonBlocks.DAMP_ROCK,
|
||||
CobblemonBlocks.DESTINY_KNOT,
|
||||
CobblemonBlocks.METRONOME,
|
||||
CobblemonBlocks.KINGS_ROCK,
|
||||
CobblemonBlocks.FLAME_ORB,
|
||||
CobblemonBlocks.LIFE_ORB,
|
||||
CobblemonBlocks.TOXIC_ORB,
|
||||
CobblemonBlocks.LIGHT_BALL,
|
||||
CobblemonBlocks.SMOKE_BALL,
|
||||
CobblemonBlocks.NEVER_MELT_ICE,
|
||||
CobblemonBlocks.WIDE_LENS,
|
||||
CobblemonBlocks.ZOOM_LENS,
|
||||
CobblemonBlocks.SCOPE_LENS
|
||||
)
|
||||
|
||||
this.createBoatModelLayers()
|
||||
@@ -405,8 +437,10 @@ object CobblemonClient {
|
||||
var renderer: LivingEntityRenderer<Player, PlayerModel<Player>>? =
|
||||
skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
|
||||
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
|
||||
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
|
||||
renderer = skinMap[PlayerSkin.Model.SLIM] as LivingEntityRenderer<Player, PlayerModel<Player>>?
|
||||
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
|
||||
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
|
||||
}
|
||||
|
||||
private fun registerMenuScreens() {
|
||||
|
||||
@@ -32,6 +32,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.ActiveAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.animation.PrimaryAnimation
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
|
||||
import com.cobblemon.mod.common.client.render.player.RecallArmAnimationTracker
|
||||
import com.cobblemon.mod.common.client.render.pokemon.PokemonRenderer.Companion.ease
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import com.cobblemon.mod.common.net.messages.server.pokemon.update.ServerboundUpdateRidingStatePacket
|
||||
@@ -320,6 +321,7 @@ class PokemonClientDelegate : PosableState(), PokemonSideDelegate {
|
||||
ballOffset = 0f
|
||||
ballRotOffset = 0f
|
||||
sendOutPosition = null
|
||||
currentEntity.ownerUUID?.let { RecallArmAnimationTracker.start(it, currentEntity.exposedBall) }
|
||||
afterOnClient(seconds = BEAM_EXTEND_TIME) {
|
||||
activeSendoutScale = 1F
|
||||
|
||||
|
||||
+1
-1
@@ -71,7 +71,7 @@ open class VariableObjectSelectionList(x: Int, y: Int, width: Int, height: Int):
|
||||
var stackHeight = -scrollAmount.toInt()
|
||||
guiGraphics.enableScissor(x, y, x + width, y + height)
|
||||
children.forEachIndexed { index, entry ->
|
||||
if (stackHeight in 1..<height || stackHeight + entry.height in 1..<height) {
|
||||
if (stackHeight < height && stackHeight + entry.height > 0) {
|
||||
entry.render(guiGraphics, index, x, y + stackHeight, mouseX, mouseY, partialTick)
|
||||
}
|
||||
stackHeight += entry.height
|
||||
|
||||
@@ -36,32 +36,33 @@ import com.cobblemon.mod.common.client.gui.handleProfileDebugKeys
|
||||
import com.cobblemon.mod.common.client.gui.logProfileTranslations
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HEADER_BAR_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.ICONS_ARROW_PADDING_AROUND
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.ICONS_PADDING_AROUND
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_ABILITIES
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_DESCRIPTION
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_DROPS
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_EVOLUTION_TREE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_ICON_SIZE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_MOVES
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_SIZE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_STATS
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.AbilitiesWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.DescriptionWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.DropsScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.EntriesScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.MovesLearnsetWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.PokemonInfoWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.SearchWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.SizeWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.StatsWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.*
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree.EvolutionTreeWidget
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree.debugEvolutionRequirementRendering
|
||||
import com.cobblemon.mod.common.client.pokedex.PokedexType
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.isInventoryKeyPressed
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import com.mojang.blaze3d.platform.InputConstants
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.gui.components.EditBox
|
||||
@@ -94,6 +95,10 @@ class PokedexGUI private constructor(
|
||||
|
||||
private val categoryBarOverlay = cobblemonResource("textures/gui/pokedex/pokedex_screen_bar_category.png")
|
||||
|
||||
// TODO: separate into smaller components / widgets?
|
||||
private val backgroundOverlay = cobblemonResource("textures/gui/pokedex/pokedex_screen_info_overlay.png")
|
||||
private val expandedBackgroundOverlay = cobblemonResource("textures/gui/pokedex/pokedex_screen_info_overlay_expanded.png")
|
||||
|
||||
private val tabSelectArrow = cobblemonResource("textures/gui/pokedex/select_arrow.png")
|
||||
private val tabIcons = arrayOf(
|
||||
cobblemonResource("textures/gui/pokedex/tab_info.png"),
|
||||
@@ -101,7 +106,8 @@ class PokedexGUI private constructor(
|
||||
cobblemonResource("textures/gui/pokedex/tab_size.png"),
|
||||
cobblemonResource("textures/gui/pokedex/tab_stats.png"),
|
||||
cobblemonResource("textures/gui/pokedex/tab_drops.png"),
|
||||
cobblemonResource("textures/gui/pokedex/tab_moves.png")
|
||||
cobblemonResource("textures/gui/pokedex/tab_moves.png"),
|
||||
cobblemonResource("textures/gui/pokedex/tab_evolution_tree.png"),
|
||||
)
|
||||
|
||||
val arrowUpIcon = cobblemonResource("textures/gui/pokedex/arrow_up.png")
|
||||
@@ -114,6 +120,8 @@ class PokedexGUI private constructor(
|
||||
TAB_MOVES
|
||||
)
|
||||
|
||||
private val ICONS_PADDING_BETWEEN = (BASE_WIDTH / 2 - ICONS_PADDING_AROUND * 2) / tabIcons.size
|
||||
|
||||
/**
|
||||
* Attempts to open this screen for a client.
|
||||
*/
|
||||
@@ -132,6 +140,7 @@ class PokedexGUI private constructor(
|
||||
|
||||
var oldDragPosX = 0.0
|
||||
var canDragRender = false
|
||||
private var activeDragableElement: GuiEventListener? = null
|
||||
|
||||
private var filteredPokedex: Collection<PokedexDef> = mutableListOf()
|
||||
private var seenCount = "0000"
|
||||
@@ -155,13 +164,29 @@ class PokedexGUI private constructor(
|
||||
private lateinit var scrollScreen: EntriesScrollingWidget
|
||||
private lateinit var pokemonInfoWidget: PokemonInfoWidget
|
||||
private lateinit var searchWidget: SearchWidget
|
||||
private lateinit var pokemonNameWidget: PokemonNameWidget
|
||||
|
||||
private var selectedSearchByType: SearchByType = SearchByType.SPECIES
|
||||
private var selectedCategoryFilter: PokedexCategoryFilterType = PokedexCategoryFilterType.ALL
|
||||
private val tabButtons: MutableList<ScaledButton> = mutableListOf()
|
||||
|
||||
var seenShinyStates = setOf<String>()
|
||||
var shiny = false
|
||||
var maleRatio = -1F
|
||||
var gender = Gender.GENDERLESS
|
||||
var aspects = mutableSetOf<String>()
|
||||
|
||||
lateinit var tabInfoElement: GuiEventListener
|
||||
var tabInfoIndex = TAB_DESCRIPTION
|
||||
var evolutionTreeExpanded = false
|
||||
set(value) {
|
||||
field = value
|
||||
|
||||
if (::pokemonInfoWidget.isInitialized) {
|
||||
pokemonInfoWidget.active = !value
|
||||
pokemonInfoWidget.visible = !value
|
||||
}
|
||||
}
|
||||
|
||||
override fun renderBlurredBackground(delta: Float) {}
|
||||
override fun renderMenuBackground(context: GuiGraphics) {}
|
||||
@@ -180,7 +205,7 @@ class PokedexGUI private constructor(
|
||||
|
||||
//Info Widget
|
||||
if (::pokemonInfoWidget.isInitialized) removeWidget(pokemonInfoWidget)
|
||||
pokemonInfoWidget = PokemonInfoWidget(x + 180, y + 28) { newForm -> updateSelectedForm(newForm) }
|
||||
pokemonInfoWidget = PokemonInfoWidget(this, x + 180, y + 28) { newForm -> updateSelectedForm(newForm) }
|
||||
addRenderableWidget(pokemonInfoWidget)
|
||||
|
||||
setUpTabs()
|
||||
@@ -192,6 +217,10 @@ class PokedexGUI private constructor(
|
||||
searchWidget = SearchWidget(x + 26, y + 28, 120, HEADER_BAR_HEIGHT, update = ::updateFilters)
|
||||
addRenderableWidget(searchWidget)
|
||||
|
||||
if (::pokemonNameWidget.isInitialized) removeWidget(pokemonNameWidget)
|
||||
pokemonNameWidget = PokemonNameWidget(this, x + 180, y + 28)
|
||||
addRenderableWidget(pokemonNameWidget)
|
||||
|
||||
if (::regionSelectWidgetUp.isInitialized) removeWidget(regionSelectWidgetUp)
|
||||
regionSelectWidgetUp = ScaledButton(
|
||||
buttonX = (x + 95).toFloat(),
|
||||
@@ -367,6 +396,14 @@ class PokedexGUI private constructor(
|
||||
shadow = true
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = if (evolutionTreeExpanded) expandedBackgroundOverlay else backgroundOverlay,
|
||||
x = pokemonInfoWidget.x, y = pokemonInfoWidget.y,
|
||||
width = HALF_OVERLAY_WIDTH,
|
||||
height = HALF_OVERLAY_HEIGHT
|
||||
)
|
||||
|
||||
// Category filter
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
@@ -402,7 +439,7 @@ class PokedexGUI private constructor(
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = tabSelectArrow,
|
||||
x = (x + 191.5F + (22F * tabInfoIndex)) / SCALE,
|
||||
x = (x + BASE_WIDTH / 2 + ICONS_ARROW_PADDING_AROUND + tabInfoIndex * ICONS_PADDING_BETWEEN) / SCALE,
|
||||
y = (y + 177) / SCALE,
|
||||
width = 12,
|
||||
height = 6,
|
||||
@@ -441,6 +478,7 @@ class PokedexGUI private constructor(
|
||||
val canDisplayEntry = true //selectedForm?.unlockForms
|
||||
|
||||
if (::pokemonInfoWidget.isInitialized
|
||||
&& pokemonInfoWidget.active
|
||||
&& !pokemonInfoWidget.suppressViewport
|
||||
&& pokemonInfoWidget.isWithinPortraitSpace(mouseX, mouseY)
|
||||
&& canDisplayEntry == true
|
||||
@@ -449,6 +487,12 @@ class PokedexGUI private constructor(
|
||||
isDragging = true
|
||||
oldDragPosX = mouseX
|
||||
playSound(CobblemonSounds.POKEDEX_CLICK_SHORT)
|
||||
activeDragableElement = pokemonInfoWidget
|
||||
} else if (tabInfoIndex == TAB_EVOLUTION_TREE &&
|
||||
::tabInfoElement.isInitialized &&
|
||||
(tabInfoElement as EvolutionTreeWidget).isHovered(mouseX, mouseY)
|
||||
) {
|
||||
activeDragableElement = tabInfoElement
|
||||
}
|
||||
return try {
|
||||
super.mouseClicked(mouseX, mouseY, button)
|
||||
@@ -460,14 +504,18 @@ class PokedexGUI private constructor(
|
||||
override fun mouseReleased(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
|
||||
if (canDragRender) canDragRender = false
|
||||
if (isDragging) isDragging = false
|
||||
activeDragableElement = null
|
||||
return super.mouseReleased(pMouseX, pMouseY, pButton)
|
||||
}
|
||||
|
||||
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
|
||||
if (isDragging && canDragRender) {
|
||||
if (isDragging && canDragRender && activeDragableElement == pokemonInfoWidget) {
|
||||
val dragOffsetY = (oldDragPosX - mouseX).toFloat()
|
||||
pokemonInfoWidget.rotationY = (((pokemonInfoWidget.rotationY + dragOffsetY) % 360 + 360) % 360)
|
||||
} else if (activeDragableElement == tabInfoElement) {
|
||||
tabInfoElement.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
oldDragPosX = mouseX
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
@@ -558,6 +606,8 @@ class PokedexGUI private constructor(
|
||||
selectedForm = CobblemonClient.clientPokedexData.getEncounteredForms(newSelectedEntry).firstOrNull()
|
||||
|
||||
pokemonInfoWidget.setDexEntry(selectedEntry!!)
|
||||
pokemonNameWidget.update(selectedEntry!!)
|
||||
|
||||
displaytabInfoElement(tabInfoIndex)
|
||||
}
|
||||
|
||||
@@ -569,7 +619,7 @@ class PokedexGUI private constructor(
|
||||
|
||||
for (i in tabIcons.indices) {
|
||||
tabButtons.add(ScaledButton(
|
||||
x + 190.5F + (i * 22F),
|
||||
x + BASE_WIDTH / 2 + ICONS_PADDING_AROUND + i * ICONS_PADDING_BETWEEN,
|
||||
y + 181.5F,
|
||||
TAB_ICON_SIZE,
|
||||
TAB_ICON_SIZE,
|
||||
@@ -582,6 +632,8 @@ class PokedexGUI private constructor(
|
||||
}
|
||||
|
||||
fun displaytabInfoElement(tabIndex: Int, update: Boolean = true) {
|
||||
if (tabIndex != TAB_EVOLUTION_TREE) evolutionTreeExpanded = false
|
||||
|
||||
val showActiveTab = selectedEntry?.let { selectedForm in getSufficientlyKnownForms(it, tabIndex) } == true
|
||||
var statSubIndex = 0
|
||||
|
||||
@@ -633,6 +685,9 @@ class PokedexGUI private constructor(
|
||||
TAB_MOVES -> {
|
||||
tabInfoElement = MovesLearnsetWidget(x + 180, y + 28)
|
||||
}
|
||||
TAB_EVOLUTION_TREE -> {
|
||||
tabInfoElement = EvolutionTreeWidget(this, x, y)
|
||||
}
|
||||
}
|
||||
val element = tabInfoElement
|
||||
if (element is Renderable && element is NarratableEntry) {
|
||||
@@ -707,6 +762,9 @@ class PokedexGUI private constructor(
|
||||
selectedForm = fallbackForm
|
||||
) { next -> cycleSelectedForm(next) }
|
||||
}
|
||||
TAB_EVOLUTION_TREE -> {
|
||||
(tabInfoElement as EvolutionTreeWidget).setup(selectedEntry, selectedForm)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (tabInfoIndex != TAB_DESCRIPTION) displaytabInfoElement(TAB_DESCRIPTION)
|
||||
@@ -770,6 +828,10 @@ class PokedexGUI private constructor(
|
||||
}
|
||||
}
|
||||
|
||||
if (tabInfoIndex == TAB_EVOLUTION_TREE && keyCode == InputConstants.KEY_SEMICOLON) {
|
||||
debugEvolutionRequirementRendering()
|
||||
}
|
||||
|
||||
return super.keyPressed(keyCode, scanCode, modifiers)
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -16,6 +16,8 @@ object PokedexGUIConstants {
|
||||
const val HEADER_BAR_HEIGHT = 11
|
||||
const val POKEMON_DESCRIPTION_HEIGHT = 42
|
||||
const val POKEMON_DESCRIPTION_PADDING = 9
|
||||
const val ICONS_PADDING_AROUND = 18.5f
|
||||
const val ICONS_ARROW_PADDING_AROUND = ICONS_PADDING_AROUND + 1
|
||||
|
||||
const val TAB_ICON_SIZE = 16
|
||||
|
||||
@@ -36,6 +38,7 @@ object PokedexGUIConstants {
|
||||
const val TAB_STATS = 3
|
||||
const val TAB_DROPS = 4
|
||||
const val TAB_MOVES = 5
|
||||
const val TAB_EVOLUTION_TREE = 6
|
||||
|
||||
// RowScrollingWidget
|
||||
const val SCROLL_BASE_HEIGHT = 141
|
||||
|
||||
+72
-9
@@ -13,38 +13,101 @@ import com.cobblemon.mod.common.api.text.font
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
|
||||
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
|
||||
|
||||
fun renderTooltip(context: GuiGraphics, text: MutableComponent, posX: Int, posY: Int, delta: Float, offsetY: Int = 0) {
|
||||
private const val TOOLTIP_SLICE_SIZE = 11
|
||||
|
||||
fun renderTooltip(
|
||||
context: GuiGraphics,
|
||||
text: MutableComponent,
|
||||
posX: Int,
|
||||
posY: Int,
|
||||
delta: Float,
|
||||
offsetY: Int = 0,
|
||||
) {
|
||||
renderTooltip(
|
||||
context,
|
||||
listOf(text),
|
||||
posX,
|
||||
posY,
|
||||
delta,
|
||||
offsetY,
|
||||
)
|
||||
}
|
||||
|
||||
fun renderTooltip(
|
||||
context: GuiGraphics,
|
||||
text: List<MutableComponent>,
|
||||
posX: Int,
|
||||
posY: Int,
|
||||
delta: Float,
|
||||
offsetY: Int = 0,
|
||||
) {
|
||||
val poseStack = context.pose()
|
||||
val textWidth = Minecraft.getInstance().font.width(text.font(CobblemonResources.DEFAULT_LARGE))
|
||||
val tooltipWidth = textWidth + 6
|
||||
val tooltipHeight = 11
|
||||
val textWidth = text.maxOf { Minecraft.getInstance().font.width(it.font(CobblemonResources.DEFAULT_LARGE)) }
|
||||
val tooltipPadding = 6
|
||||
val tooltipWidth = textWidth + tooltipPadding
|
||||
val tooltipTop = posY + offsetY
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, 0.0, 1000.0)
|
||||
|
||||
Minecraft.getInstance().let {
|
||||
it.mainRenderTarget.bindWrite(false)
|
||||
context.enableScissor(
|
||||
posX - (tooltipWidth / 2),
|
||||
tooltipTop + 1,
|
||||
(posX - (tooltipWidth / 2)) + tooltipWidth,
|
||||
tooltipTop + 10
|
||||
tooltipTop + TOOLTIP_SLICE_SIZE * text.size - 1
|
||||
)
|
||||
it.gameRenderer.processBlurEffect(delta)
|
||||
context.disableScissor()
|
||||
it.mainRenderTarget.bindWrite(true)
|
||||
}
|
||||
|
||||
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX - (tooltipWidth / 2) - 1, y = tooltipTop, width = 1, height = tooltipHeight)
|
||||
blitk(matrixStack = poseStack, texture = tooltipBackground, x = posX - (tooltipWidth / 2), y = tooltipTop, width = tooltipWidth, height = tooltipHeight)
|
||||
blitk(matrixStack = poseStack, texture = tooltipEdge, x = posX + (tooltipWidth / 2), y = tooltipTop, width = 1, height = tooltipHeight)
|
||||
drawScaledText(context = context, font = CobblemonResources.DEFAULT_LARGE, text = text, x = posX, y = tooltipTop + 1, shadow = true, centered = true)
|
||||
val totalHeight = TOOLTIP_SLICE_SIZE * text.size
|
||||
|
||||
blitSlicedVertical(poseStack, tooltipEdge, posX - (tooltipWidth / 2) - 1, tooltipTop, 1, totalHeight)
|
||||
blitSlicedVertical(poseStack, tooltipBackground, posX - (tooltipWidth / 2), tooltipTop, tooltipWidth, totalHeight)
|
||||
blitSlicedVertical(poseStack, tooltipEdge, posX + (tooltipWidth / 2), tooltipTop, 1, totalHeight)
|
||||
|
||||
val centered = text.size == 1
|
||||
|
||||
text.forEachIndexed { index, it ->
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = it,
|
||||
x = if (centered) posX else posX - tooltipWidth / 2 + tooltipPadding / 2,
|
||||
y = tooltipTop + 1 + TOOLTIP_SLICE_SIZE * index,
|
||||
shadow = true,
|
||||
centered = centered
|
||||
)
|
||||
}
|
||||
|
||||
poseStack.popPose()
|
||||
}
|
||||
|
||||
// Stretching the whole 11px image over an arbitrary height thickens the caps as more lines are added, so
|
||||
// instead the caps are drawn 1:1 and only the flat fill row is stretched to cover the remaining height
|
||||
private fun blitSlicedVertical(
|
||||
poseStack: PoseStack,
|
||||
texture: ResourceLocation,
|
||||
x: Number,
|
||||
y: Int,
|
||||
width: Number,
|
||||
totalHeight: Int,
|
||||
) {
|
||||
val middleHeight = totalHeight - 2
|
||||
|
||||
blitk(matrixStack = poseStack, texture = texture, x = x, y = y, width = width, height = 1, vOffset = 0, textureHeight = TOOLTIP_SLICE_SIZE)
|
||||
blitk(matrixStack = poseStack, texture = texture, x = x, y = y + 1, width = width, height = middleHeight, vOffset = middleHeight, textureHeight = TOOLTIP_SLICE_SIZE * middleHeight)
|
||||
blitk(matrixStack = poseStack, texture = texture, x = x, y = y + 1 + middleHeight, width = width, height = 1, vOffset = TOOLTIP_SLICE_SIZE - 1, textureHeight = TOOLTIP_SLICE_SIZE)
|
||||
}
|
||||
@@ -20,8 +20,8 @@ import net.minecraft.client.sounds.SoundManager
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
class ScaledButton(
|
||||
var buttonX: Float,
|
||||
var buttonY: Float,
|
||||
buttonX: Float,
|
||||
buttonY: Float,
|
||||
val buttonWidth: Number,
|
||||
val buttonHeight: Number,
|
||||
var resource: ResourceLocation? = null,
|
||||
@@ -30,6 +30,18 @@ class ScaledButton(
|
||||
val clickAction: Button.OnPress
|
||||
): Button(buttonX.toInt(), buttonY.toInt(), buttonWidth.toInt(), buttonHeight.toInt(), "".text(), clickAction, DEFAULT_NARRATION), CobblemonRenderable {
|
||||
|
||||
var buttonX: Float = buttonX
|
||||
set(value) {
|
||||
field = value
|
||||
this.x = value.toInt()
|
||||
}
|
||||
|
||||
var buttonY: Float = buttonY
|
||||
set(value) {
|
||||
field = value
|
||||
this.y = value.toInt()
|
||||
}
|
||||
|
||||
var isWidgetActive = false
|
||||
var isVisible = true
|
||||
|
||||
|
||||
+27
-126
@@ -12,14 +12,13 @@ import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.ClientMoLangFunctions.setupClient
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.gui.ScrollingWidget
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_BAR_WIDTH
|
||||
@@ -27,19 +26,14 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_BA
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_SLOT_SIZE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_SLOT_SPACING
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.EntriesScrollingWidget.PokemonScrollSlotRow
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.util.Mth
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class EntriesScrollingWidget(val pX: Int, val pY: Int, val setPokedexEntry: (PokedexEntry) -> (Unit)): ScrollingWidget<PokemonScrollSlotRow>(
|
||||
width = PokedexGUIConstants.HALF_OVERLAY_WIDTH,
|
||||
@@ -135,13 +129,6 @@ class EntriesScrollingWidget(val pX: Int, val pY: Int, val setPokedexEntry: (Pok
|
||||
val discoveryLevelList: MutableList<PokedexEntryProgress>,
|
||||
val setPokedexEntry : (PokedexEntry) -> (Unit)
|
||||
): Slot<PokemonScrollSlotRow>() {
|
||||
companion object {
|
||||
private val slotResource = cobblemonResource("textures/gui/pokedex/pokedex_slot.png")
|
||||
private val slotHighlight = cobblemonResource("textures/gui/pokedex/slot_select.png")
|
||||
private val caughtIcon = cobblemonResource("textures/gui/pokedex/caught_icon_small.png")
|
||||
private val unknownIcon = cobblemonResource("textures/gui/pokedex/pokedex_slot_unknown.png")
|
||||
private val unimplementedIcon = cobblemonResource("textures/gui/pokedex/pokedex_slot_unimplemented.png")
|
||||
}
|
||||
val runtime = MoLangRuntime().setupClient().setup().also {
|
||||
it.environment.query.addFunction("get_pokedex") { CobblemonClient.clientPokedexData.struct }
|
||||
}
|
||||
@@ -161,128 +148,42 @@ class EntriesScrollingWidget(val pX: Int, val pY: Int, val setPokedexEntry: (Pok
|
||||
hovered: Boolean,
|
||||
tickDelta: Float
|
||||
) {
|
||||
dexDataList.forEachIndexed { index, dexData ->
|
||||
val state = FloatingState()
|
||||
val species = PokemonSpecies.getByIdentifier(dexData.speciesId)
|
||||
//FIXME: This may not work properly when accounting for custom pokemon with the same dex number
|
||||
val pokemonNumber = (species?.nationalPokedexNumber?.toString() ?: "0").padStart(4, '0')
|
||||
|
||||
val speciesNumber = pokemonNumber.text()
|
||||
dexDataList.forEachIndexed { index, pokedexEntry ->
|
||||
val species = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId)
|
||||
val discoveryLevel = discoveryLevelList[index]
|
||||
val firstVisibleForm = CobblemonClient.clientPokedexData.getEncounteredForms(dexData).firstOrNull()
|
||||
val shouldDrawMon = firstVisibleForm != null
|
||||
val firstVisibleForm = CobblemonClient.clientPokedexData.getEncounteredForms(pokedexEntry).firstOrNull()
|
||||
|
||||
if (species == null) {
|
||||
return@forEachIndexed
|
||||
}
|
||||
|
||||
val matrices = context.pose()
|
||||
val renderablePokemon =
|
||||
if (firstVisibleForm != null) {
|
||||
val firstVisibleGender = CobblemonClient.clientPokedexData.getSeenGenders(pokedexEntry, firstVisibleForm).firstOrNull()
|
||||
val shinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, firstVisibleForm)
|
||||
val firstVisibleShiny = shinyStates.count() == 1 && shinyStates.first() == "shiny"
|
||||
val formAspects = species.forms.firstOrNull { it.name.equals(firstVisibleForm.displayForm, ignoreCase = true) }?.aspects ?: species.standardForm.aspects
|
||||
val seenAspects = CobblemonClient.clientPokedexData.getSeenAspects(pokedexEntry)
|
||||
val variationAspects = pokedexEntry.variations.mapNotNull { it.aspects.firstOrNull { it in seenAspects } }
|
||||
val aspectsToDraw = (pokedexEntry.displayAspects + variationAspects + formAspects + (firstVisibleGender ?: Gender.GENDERLESS).name.lowercase()).toMutableSet()
|
||||
if (firstVisibleShiny) {
|
||||
aspectsToDraw.add("shiny")
|
||||
}
|
||||
|
||||
val startPosX = x + ((SCROLL_SLOT_SPACING + SCROLL_SLOT_SIZE) * index)
|
||||
val startPosY = y + SCROLL_SLOT_SPACING + 1
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotResource,
|
||||
x = startPosX,
|
||||
y = startPosY,
|
||||
width = SCROLL_SLOT_SIZE,
|
||||
height = SCROLL_SLOT_SIZE
|
||||
)
|
||||
|
||||
if (getHoveredSlotIndex(mouseX, mouseY) == index) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotHighlight,
|
||||
x = startPosX,
|
||||
y = startPosY,
|
||||
width = SCROLL_SLOT_SIZE,
|
||||
height = SCROLL_SLOT_SIZE,
|
||||
vOffset = SCROLL_SLOT_SIZE,
|
||||
textureHeight = SCROLL_SLOT_SIZE * 2
|
||||
)
|
||||
}
|
||||
|
||||
if (shouldDrawMon) {
|
||||
val firstVisibleGender = CobblemonClient.clientPokedexData.getSeenGenders(dexData, firstVisibleForm).firstOrNull()
|
||||
val shinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(dexData, firstVisibleForm)
|
||||
val firstVisibleShiny = shinyStates.count() == 1 && shinyStates.first() == "shiny"
|
||||
val formAspects = species.forms.firstOrNull { it.name.equals(firstVisibleForm.displayForm, ignoreCase = true) }?.aspects ?: species.standardForm.aspects
|
||||
val seenAspects = CobblemonClient.clientPokedexData.getSeenAspects(dexData)
|
||||
val variationAspects = dexData.variations.mapNotNull { it.aspects.firstOrNull { it in seenAspects } }
|
||||
context.enableScissor(
|
||||
startPosX + 1,
|
||||
startPosY + 1,
|
||||
startPosX + SCROLL_SLOT_SIZE - 1,
|
||||
startPosY + SCROLL_SLOT_SIZE - 2
|
||||
)
|
||||
val aspectsToDraw = (dexData.displayAspects + variationAspects + formAspects + (firstVisibleGender ?: Gender.GENDERLESS).name.lowercase()).toMutableSet()
|
||||
if (firstVisibleShiny) {
|
||||
aspectsToDraw.add("shiny")
|
||||
RenderablePokemon(species, aspectsToDraw)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
state.currentAspects = aspectsToDraw
|
||||
matrices.pushPose()
|
||||
matrices.translate(startPosX + (SCROLL_SLOT_SIZE / 2.0), startPosY + 1.0, 0.0)
|
||||
matrices.scale(2.5F, 2.5F, 1F)
|
||||
drawProfilePokemon(
|
||||
renderablePokemon = RenderablePokemon(species, aspectsToDraw),
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
partialTicks = 0F,
|
||||
scale = 4.5F
|
||||
)
|
||||
matrices.popPose()
|
||||
context.disableScissor()
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = unknownIcon,
|
||||
x = startPosX + 8.5,
|
||||
y = startPosY + 9,
|
||||
width = 8,
|
||||
height = 10
|
||||
)
|
||||
|
||||
if (!species.implemented) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = unimplementedIcon,
|
||||
x = (startPosX + 14) / SCALE,
|
||||
y = (startPosY + 15.5) / SCALE,
|
||||
width = 7,
|
||||
height = 7,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure elements are not hidden behind Pokémon render
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 100.0)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = speciesNumber,
|
||||
x = startPosX + 1.5,//2,
|
||||
y = startPosY + 2.5,//2
|
||||
shadow = true,
|
||||
scale = SCALE
|
||||
PokemonSlotWidget.render(
|
||||
context,
|
||||
x = x + (SCROLL_SLOT_SPACING + SCROLL_SLOT_SIZE) * index,
|
||||
y = y + SCROLL_SLOT_SPACING + 1,
|
||||
getHoveredSlotIndex(mouseX, mouseY) == index,
|
||||
species,
|
||||
renderablePokemon,
|
||||
discoveryLevel,
|
||||
)
|
||||
|
||||
if (discoveryLevel == PokedexEntryProgress.OWNED) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = caughtIcon,
|
||||
x = (startPosX + 18) / SCALE,
|
||||
y = (startPosY + 1.5) / SCALE,
|
||||
width = 11,
|
||||
height = 11,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
matrices.popPose()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+27
-65
@@ -10,10 +10,10 @@ package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexCosmeticVariation
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
@@ -25,8 +25,9 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.ProfileTransformType
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_PORTRAIT_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_PORTRAIT_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.PORTRAIT_POKE_BALL_HEIGHT
|
||||
@@ -52,15 +53,13 @@ import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.client.resources.sounds.SimpleSoundInstance
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.sounds.SoundEvent
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm) -> (Unit)) : SoundlessWidget(
|
||||
class PokemonInfoWidget(val parent: PokedexGUI, val pX: Int, val pY: Int, val updateForm: (PokedexForm) -> (Unit)) : SoundlessWidget(
|
||||
pX,
|
||||
pY,
|
||||
POKEMON_PORTRAIT_WIDTH,
|
||||
@@ -82,7 +81,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
private val arrowFormLeft = cobblemonResource("textures/gui/pokedex/forms_arrow_left.png")
|
||||
private val arrowFormRight = cobblemonResource("textures/gui/pokedex/forms_arrow_right.png")
|
||||
|
||||
private val caughtIcon = cobblemonResource("textures/gui/pokedex/caught_icon.png")
|
||||
private val typeBar = cobblemonResource("textures/gui/pokedex/type_bar.png")
|
||||
private val typeBarDouble = cobblemonResource("textures/gui/pokedex/type_bar_double.png")
|
||||
|
||||
@@ -100,9 +98,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
var currentEntry : PokedexEntry? = null
|
||||
|
||||
var speciesName: MutableComponent = Component.translatable("")
|
||||
var speciesNumber: MutableComponent = "0000".text()
|
||||
|
||||
val runtime = MoLangRuntime().setupClient().setup().also {
|
||||
it.environment.query.addFunction("get_pokedex") { CobblemonClient.clientPokedexData.struct }
|
||||
}
|
||||
@@ -111,10 +106,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
var selectedFormIndex: Int = 0
|
||||
|
||||
var type: Array<ElementalType?> = arrayOf(null, null)
|
||||
var seenShinyStates: Set<String> = setOf()
|
||||
var shiny = false
|
||||
var maleRatio = -1F
|
||||
var gender: Gender = Gender.GENDERLESS
|
||||
|
||||
var renderablePokemon : RenderablePokemon? = null
|
||||
|
||||
@@ -195,7 +186,10 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
20,
|
||||
resource = buttonGenderMale,
|
||||
clickAction = {
|
||||
if (maleRatio > 0 && maleRatio < 1) gender = if (gender == Gender.MALE) Gender.FEMALE else Gender.MALE
|
||||
val maleRatio = parent.maleRatio
|
||||
if (maleRatio > 0 && maleRatio < 1) {
|
||||
parent.gender = if (parent.gender == Gender.MALE) Gender.FEMALE else Gender.MALE
|
||||
}
|
||||
updateAspects()
|
||||
}
|
||||
).apply { addWidget(this) }
|
||||
@@ -207,8 +201,8 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
20,
|
||||
buttonNone,
|
||||
clickAction = {
|
||||
if (seenShinyStates.size > 1) {
|
||||
shiny = !shiny
|
||||
if (parent.seenShinyStates.size > 1) {
|
||||
parent.shiny = !parent.shiny
|
||||
updateAspects()
|
||||
}
|
||||
}
|
||||
@@ -263,8 +257,7 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
|
||||
val currentEntry = this.currentEntry ?: return
|
||||
|
||||
val hasKnowledge =
|
||||
CobblemonClient.clientPokedexData.getKnowledgeForSpecies(currentEntry.speciesId) != PokedexEntryProgress.UNREGISTERED
|
||||
val hasKnowledge = CobblemonClient.clientPokedexData.getKnowledgeForSpecies(currentEntry.speciesId) != PokedexEntryProgress.UNREGISTERED
|
||||
val species = currentEntry.speciesId.let { PokemonSpecies.getByIdentifier(it) } ?: return
|
||||
|
||||
val matrices = context.pose()
|
||||
@@ -277,37 +270,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
height = HALF_OVERLAY_HEIGHT
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesNumber.bold(),
|
||||
x = pX + 3,
|
||||
y = pY + 1,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = (if (hasKnowledge) speciesName else lang("ui.generic.question_marks")).bold(),
|
||||
x = pX + 26,
|
||||
y = pY + 1,
|
||||
colour = 0x606B6E
|
||||
)
|
||||
|
||||
// Caught icon
|
||||
if (isSelectedPokemonOwned()) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = caughtIcon,
|
||||
x = (pX + 129) / SCALE,
|
||||
y = (pY + 2) / SCALE,
|
||||
width = 14,
|
||||
height = 14,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
if (!suppressViewport) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
@@ -571,8 +533,6 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
if (species != null) {
|
||||
this.visibleForms = CobblemonClient.clientPokedexData.getEncounteredForms(pokedexEntry).toMutableList()
|
||||
this.speciesNumber = species.nationalPokedexNumber.toString().padStart(4, '0').text()
|
||||
this.speciesName = species.translatedName
|
||||
|
||||
if (forms.isNotEmpty()) {
|
||||
formLeftButton.active = true
|
||||
@@ -598,23 +558,23 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
variationButtons.forEach { removeWidget(it.getWidget()) }
|
||||
variationButtons.clear()
|
||||
|
||||
seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, pokedexForm)
|
||||
shiny = seenShinyStates.count() == 1 && seenShinyStates.first() == "shiny"
|
||||
shinyButton.resource = if (shiny) buttonShiny else buttonNone
|
||||
shinyButton.visible = shiny || seenShinyStates.size > 1
|
||||
shinyButton.active = seenShinyStates.size > 1
|
||||
parent.seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, pokedexForm)
|
||||
parent.shiny = parent.seenShinyStates.count() == 1 && parent.seenShinyStates.first() == "shiny"
|
||||
shinyButton.resource = if (parent.shiny) buttonShiny else buttonNone
|
||||
shinyButton.visible = parent.shiny || parent.seenShinyStates.size > 1
|
||||
shinyButton.active = parent.seenShinyStates.size > 1
|
||||
|
||||
val form = species.forms.find { it.name.equals(pokedexForm.displayForm, ignoreCase = true) } ?: species.standardForm
|
||||
maleRatio = form.maleRatio
|
||||
parent.maleRatio = form.maleRatio
|
||||
|
||||
val seenGenders = CobblemonClient.clientPokedexData.getSeenGenders(pokedexEntry, pokedexForm)
|
||||
val visibleGenders = seenGenders.filter { it != Gender.GENDERLESS }
|
||||
if (visibleGenders.isEmpty()) {
|
||||
gender = Gender.GENDERLESS
|
||||
parent.gender = Gender.GENDERLESS
|
||||
genderButton.visible = false
|
||||
genderButton.active = false
|
||||
} else {
|
||||
gender = visibleGenders.first()
|
||||
parent.gender = visibleGenders.first()
|
||||
genderButton.visible = true
|
||||
genderButton.active = visibleGenders.size > 1
|
||||
}
|
||||
@@ -693,8 +653,8 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
return
|
||||
}
|
||||
|
||||
genderButton.resource = if (gender == Gender.FEMALE) buttonGenderFemale else buttonGenderMale
|
||||
shinyButton.resource = if (shiny) buttonShiny else buttonNone
|
||||
genderButton.resource = if (parent.gender == Gender.FEMALE) buttonGenderFemale else buttonGenderMale
|
||||
shinyButton.resource = if (parent.shiny) buttonShiny else buttonNone
|
||||
|
||||
val species = currentEntry?.speciesId?.let { PokemonSpecies.getByIdentifier(it) }
|
||||
if (species != null) {
|
||||
@@ -703,12 +663,14 @@ class PokemonInfoWidget(val pX: Int, val pY: Int, val updateForm: (PokedexForm)
|
||||
|
||||
updateType(species, form)
|
||||
|
||||
val aspects = mutableSetOf<String>()
|
||||
if (shiny) aspects.add(SHINY_ASPECT.aspect)
|
||||
val aspects = parent.aspects
|
||||
aspects.clear()
|
||||
|
||||
if (gender == Gender.FEMALE) {
|
||||
if (parent.shiny) aspects.add(SHINY_ASPECT.aspect)
|
||||
|
||||
if (parent.gender == Gender.FEMALE) {
|
||||
aspects.add("female")
|
||||
} else if (gender == Gender.MALE) {
|
||||
} else if (parent.gender == Gender.MALE) {
|
||||
aspects.add("male")
|
||||
}
|
||||
|
||||
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_PORTRAIT_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_PORTRAIT_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
class PokemonNameWidget(
|
||||
val parent: PokedexGUI,
|
||||
val pX: Int,
|
||||
val pY: Int,
|
||||
) : SoundlessWidget(
|
||||
pX,
|
||||
pY,
|
||||
POKEMON_PORTRAIT_WIDTH,
|
||||
POKEMON_PORTRAIT_HEIGHT,
|
||||
lang("ui.pokedex.pokemon_name"),
|
||||
) {
|
||||
|
||||
companion object {
|
||||
private val caughtIcon = cobblemonResource("textures/gui/pokedex/caught_icon.png")
|
||||
}
|
||||
|
||||
var hasKnowledge: Boolean = false
|
||||
var isOwned: Boolean = false
|
||||
var speciesName: MutableComponent = Component.translatable("")
|
||||
var speciesNumber: MutableComponent = "0000".text()
|
||||
|
||||
override fun renderWidget(
|
||||
context: GuiGraphics,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
partialTick: Float
|
||||
) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesNumber.bold(),
|
||||
x = pX + 3,
|
||||
y = pY + 1,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
if (hasKnowledge) {
|
||||
drawScaledText(
|
||||
context = context,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = speciesName.bold(),
|
||||
x = pX + 26,
|
||||
y = pY + 1,
|
||||
colour = 0x606B6E
|
||||
)
|
||||
}
|
||||
|
||||
if (isOwned) {
|
||||
blitk(
|
||||
matrixStack = context.pose(),
|
||||
texture = caughtIcon,
|
||||
x = (pX + 129) / SCALE,
|
||||
y = (pY + 2) / SCALE,
|
||||
width = 14,
|
||||
height = 14,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun update(pokedexEntry: PokedexEntry) {
|
||||
val species = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId) ?: return
|
||||
|
||||
val progress = CobblemonClient.clientPokedexData.getKnowledgeForSpecies(pokedexEntry.speciesId)
|
||||
this.hasKnowledge = progress != PokedexEntryProgress.UNREGISTERED
|
||||
this.isOwned = progress == PokedexEntryProgress.OWNED
|
||||
this.speciesNumber = species.nationalPokedexNumber.toString().padStart(4, '0').text()
|
||||
this.speciesName = species.translatedName
|
||||
}
|
||||
}
|
||||
+151
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets
|
||||
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCALE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_SLOT_SIZE
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.pokemon.Species
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
abstract class PokemonSlotWidget {
|
||||
companion object {
|
||||
private val slotResource = cobblemonResource("textures/gui/pokedex/pokedex_slot.png")
|
||||
private val slotHighlight = cobblemonResource("textures/gui/pokedex/slot_select.png")
|
||||
private val caughtIcon = cobblemonResource("textures/gui/pokedex/caught_icon_small.png")
|
||||
private val unknownIcon = cobblemonResource("textures/gui/pokedex/pokedex_slot_unknown.png")
|
||||
private val unimplementedIcon = cobblemonResource("textures/gui/pokedex/pokedex_slot_unimplemented.png")
|
||||
|
||||
fun render(
|
||||
context: GuiGraphics,
|
||||
x: Int,
|
||||
y: Int,
|
||||
isHovered: Boolean,
|
||||
species: Species,
|
||||
renderablePokemon: RenderablePokemon?,
|
||||
discoveryLevel: PokedexEntryProgress,
|
||||
) {
|
||||
val shouldDrawMon = renderablePokemon != null
|
||||
|
||||
// FIXME: This may not work properly when accounting for custom pokemon with the same dex number
|
||||
val pokemonNumber = species.nationalPokedexNumber.toString().padStart(4, '0')
|
||||
val speciesNumber = pokemonNumber.text()
|
||||
|
||||
val matrices = context.pose()
|
||||
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotResource,
|
||||
x = x,
|
||||
y = y,
|
||||
width = SCROLL_SLOT_SIZE,
|
||||
height = SCROLL_SLOT_SIZE,
|
||||
)
|
||||
|
||||
if (isHovered) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = slotHighlight,
|
||||
x = x,
|
||||
y = y,
|
||||
width = SCROLL_SLOT_SIZE,
|
||||
height = SCROLL_SLOT_SIZE,
|
||||
vOffset = SCROLL_SLOT_SIZE,
|
||||
textureHeight = SCROLL_SLOT_SIZE * 2,
|
||||
)
|
||||
}
|
||||
|
||||
if (shouldDrawMon && discoveryLevel != PokedexEntryProgress.UNREGISTERED) {
|
||||
context.enableScissor(
|
||||
x + 1,
|
||||
y + 1,
|
||||
x + SCROLL_SLOT_SIZE - 1,
|
||||
y + SCROLL_SLOT_SIZE - 2,
|
||||
)
|
||||
|
||||
matrices.pushPose()
|
||||
matrices.translate(x + SCROLL_SLOT_SIZE / 2.0, y + 1.0, 0.0)
|
||||
matrices.scale(2.5F, 2.5F, 1F)
|
||||
|
||||
val state = FloatingState()
|
||||
state.currentAspects = renderablePokemon.aspects
|
||||
|
||||
drawProfilePokemon(
|
||||
renderablePokemon = renderablePokemon,
|
||||
matrixStack = matrices,
|
||||
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
|
||||
state = state,
|
||||
partialTicks = 0F,
|
||||
scale = 4.5F
|
||||
)
|
||||
|
||||
matrices.popPose()
|
||||
context.disableScissor()
|
||||
} else {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = unknownIcon,
|
||||
x = x + 8.5,
|
||||
y = y + 9,
|
||||
width = 8,
|
||||
height = 10
|
||||
)
|
||||
|
||||
if (!species.implemented) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = unimplementedIcon,
|
||||
x = (x + 14) / SCALE,
|
||||
y = (y + 15.5) / SCALE,
|
||||
width = 7,
|
||||
height = 7,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// Ensure elements are not hidden behind Pokémon render
|
||||
matrices.pushPose()
|
||||
matrices.translate(0.0, 0.0, 100.0)
|
||||
|
||||
drawScaledText(
|
||||
context = context,
|
||||
text = speciesNumber,
|
||||
x = x + 1.5,
|
||||
y = y + 2.5,
|
||||
shadow = true,
|
||||
scale = SCALE
|
||||
)
|
||||
|
||||
if (discoveryLevel == PokedexEntryProgress.OWNED) {
|
||||
blitk(
|
||||
matrixStack = matrices,
|
||||
texture = caughtIcon,
|
||||
x = (x + 18) / SCALE,
|
||||
y = (y + 1.5) / SCALE,
|
||||
width = 11,
|
||||
height = 11,
|
||||
scale = SCALE
|
||||
)
|
||||
}
|
||||
|
||||
matrices.popPose()
|
||||
}
|
||||
}
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
|
||||
|
||||
import com.cobblemon.mod.common.api.pokemon.Natures
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.StringSpeciesFeature
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.text.add
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.locale.Language
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.world.item.Items
|
||||
|
||||
internal fun describePokemonProperties(properties: PokemonProperties): MutableComponent? {
|
||||
if (hasUndescribedProperty(properties)) {
|
||||
return null
|
||||
}
|
||||
|
||||
val descriptors = mutableListOf<MutableComponent>()
|
||||
|
||||
properties.species?.let { PokemonSpecies.getByIdentifier(cobblemonResource(it)) }?.let {
|
||||
descriptors.add(it.translatedName.plainCopy().green())
|
||||
}
|
||||
properties.gender?.let {
|
||||
descriptors.add(lang("gender.${it.name.lowercase()}").green())
|
||||
}
|
||||
properties.nickname?.let {
|
||||
descriptors.add(lang("ui.pokedex.requirement.properties.nickname", it.plainCopy().green()))
|
||||
}
|
||||
properties.type?.let {
|
||||
val typeName = ElementalTypes.get(it)?.displayName?.plainCopy() ?: Component.literal(it)
|
||||
descriptors.add(lang("ui.pokedex.requirement.properties.type", typeName.green()))
|
||||
}
|
||||
properties.nature?.let {
|
||||
val natureName = Natures.getNature(it.asIdentifierDefaultingNamespace())?.displayName?.asTranslated() ?: Component.literal(it)
|
||||
descriptors.add(lang("ui.pokedex.requirement.properties.nature", natureName.green()))
|
||||
}
|
||||
properties.customProperties.forEach { customProperty ->
|
||||
describeCustomProperty(customProperty)?.let { descriptors.add(it) }
|
||||
}
|
||||
|
||||
val heldItemDescriptor = properties.heldItem?.let { heldItem ->
|
||||
val itemId = heldItem.substringBefore('[').trim().asIdentifierDefaultingNamespace()
|
||||
val item = BuiltInRegistries.ITEM.get(itemId)
|
||||
if (item == Items.AIR) null else lang("ui.pokedex.requirement.properties.held_item", item.description.plainCopy().green())
|
||||
}
|
||||
|
||||
if (descriptors.isEmpty() && heldItemDescriptor == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val mainDescriptor = when {
|
||||
descriptors.isEmpty() -> null
|
||||
descriptors.size == 1 -> descriptors[0]
|
||||
else -> {
|
||||
val allButLast = descriptors.dropLast(1).reduce { acc, next -> acc.add(", ").add(next) }
|
||||
lang("ui.pokedex.requirement.properties.and", allButLast, descriptors.last())
|
||||
}
|
||||
}
|
||||
|
||||
return when {
|
||||
mainDescriptor != null && heldItemDescriptor != null -> mainDescriptor.add(" ").add(heldItemDescriptor)
|
||||
mainDescriptor != null -> mainDescriptor
|
||||
else -> heldItemDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
private fun hasUndescribedProperty(properties: PokemonProperties): Boolean {
|
||||
return properties.form != null ||
|
||||
properties.shiny != null ||
|
||||
properties.level != null ||
|
||||
properties.friendship != null ||
|
||||
properties.fullness != null ||
|
||||
properties.pokeball != null ||
|
||||
properties.ability != null ||
|
||||
properties.status != null ||
|
||||
properties.teraType != null ||
|
||||
properties.dmaxLevel != null ||
|
||||
properties.gmaxFactor != null ||
|
||||
properties.tradeable != null ||
|
||||
properties.originalTrainerType != null ||
|
||||
properties.originalTrainer != null ||
|
||||
properties.moves != null ||
|
||||
properties.movesetBuilders != null ||
|
||||
properties.cosmeticItem != null ||
|
||||
properties.scaleModifier != null ||
|
||||
properties.isAlpha != null ||
|
||||
properties.minPerfectIVs != null ||
|
||||
properties.customProperties.any { describeCustomProperty(it) == null }
|
||||
}
|
||||
|
||||
private fun describeCustomProperty(property: CustomPokemonProperty): MutableComponent? {
|
||||
return when (property) {
|
||||
is IntSpeciesFeature -> describeIntSpeciesFeature(property)
|
||||
is StringSpeciesFeature -> describeStringSpeciesFeature(property)
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun describeIntSpeciesFeature(property: IntSpeciesFeature): MutableComponent? {
|
||||
val value = Component.literal(property.value.toString())
|
||||
|
||||
val overrideKey = "ui.pokedex.requirement.properties.custom.${property.name}"
|
||||
if (Language.getInstance().has("cobblemon.$overrideKey")) {
|
||||
return lang(overrideKey, value.green())
|
||||
}
|
||||
|
||||
val displayName = (SpeciesFeatures.getFeature(property.name) as? IntSpeciesFeatureProvider)
|
||||
?.display?.name?.takeIf { it.isNotBlank() }
|
||||
?: return null
|
||||
|
||||
return lang("ui.pokedex.requirement.properties.int_feature", value.green(), displayName.asTranslated().green())
|
||||
}
|
||||
|
||||
private fun describeStringSpeciesFeature(property: StringSpeciesFeature): MutableComponent? {
|
||||
val key = "ui.pokedex.requirement.properties.custom.${property.name}"
|
||||
if (!Language.getInstance().has("cobblemon.$key")) {
|
||||
return null
|
||||
}
|
||||
|
||||
val value = PokemonSpecies.getByIdentifier(cobblemonResource(property.value))?.translatedName?.plainCopy()
|
||||
?: Component.literal(property.value)
|
||||
|
||||
return lang(key, value.green())
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.chat.Component
|
||||
|
||||
fun debugEvolutionRequirementRendering() {
|
||||
if (!Cobblemon.config.enableDebugKeys) {
|
||||
return
|
||||
}
|
||||
|
||||
val gaps = mutableListOf<Pair<String, String>>()
|
||||
|
||||
for (species in PokemonSpecies.implemented) {
|
||||
val evolutions = species.evolutions + species.forms.flatMap { it.evolutions }
|
||||
for (evolution in evolutions) {
|
||||
if (renderEvolutionData(evolution) == null) {
|
||||
gaps.add("Evolution: ${evolution::class.simpleName}" to species.showdownId())
|
||||
}
|
||||
for (requirement in evolution.requirements) {
|
||||
if (resolveRequirementData(requirement) == null) {
|
||||
gaps.add("Requirement: ${requirement::class.simpleName}" to species.showdownId())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val player = Minecraft.getInstance().player
|
||||
if (gaps.isEmpty()) {
|
||||
player?.sendSystemMessage(Component.literal("Evolution render debug: no gaps found."))
|
||||
return
|
||||
}
|
||||
|
||||
val byType = gaps.groupBy({ it.first }, { it.second })
|
||||
println("Evolution render debug found ${gaps.size} gap(s) across ${byType.size} type(s):")
|
||||
byType.forEach { (type, species) -> println(" $type: ${species.distinct().joinToString(", ")}") }
|
||||
|
||||
player?.sendSystemMessage(Component.literal("Evolution render debug: ${gaps.size} gap(s) across ${byType.size} type(s), see log for details."))
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.DummyEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
|
||||
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.itemRegistry
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.world.item.Item
|
||||
|
||||
data class RenderEvolutionData(
|
||||
val icons: List<Item>,
|
||||
val tooltips: List<MutableComponent>,
|
||||
)
|
||||
|
||||
internal fun resolveRequirementData(requirement: Requirement): RenderRequirementData? {
|
||||
val icon = requirement.pokedexIcon?.let {
|
||||
Minecraft.getInstance()?.player?.level()?.itemRegistry?.get(it)
|
||||
}
|
||||
|
||||
val tooltip = requirement.pokedexTooltip?.let {
|
||||
Component.translatable(it)
|
||||
}
|
||||
|
||||
if (icon != null && tooltip != null) {
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
return renderRequirementData(requirement)
|
||||
}
|
||||
|
||||
fun renderEvolutionData(evolution: Evolution): RenderEvolutionData? {
|
||||
when (evolution) {
|
||||
is BlockClickEvolution -> return renderBlockClickEvolution(evolution)
|
||||
is DummyEvolution -> return renderDummyEvolution(evolution)
|
||||
is ItemInteractionEvolution -> return renderItemInteractionEvolution(evolution)
|
||||
is LevelUpEvolution -> return renderLevelUpEvolution(evolution)
|
||||
is TradeEvolution -> return renderTradeEvolution(evolution)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun renderBlockClickEvolution(blockClickEvolution: BlockClickEvolution): RenderEvolutionData? {
|
||||
return null
|
||||
}
|
||||
|
||||
// TODO: Use dummyEvolution?
|
||||
fun renderDummyEvolution(evolution: Evolution): RenderEvolutionData? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun renderItemInteractionEvolution(itemInteractionEvolution: ItemInteractionEvolution): RenderEvolutionData? {
|
||||
val item = itemInteractionEvolution.requiredContext.items.get().first().value()
|
||||
val icons = mutableListOf(item)
|
||||
val tooltips =
|
||||
mutableListOf(lang("ui.pokedex.evolution.item_interact.tooltip", item.description.plainCopy().green()))
|
||||
|
||||
val requirementsDataList = itemInteractionEvolution.requirements.mapNotNull { requirement ->
|
||||
resolveRequirementData(requirement)
|
||||
}
|
||||
|
||||
requirementsDataList.forEach { requirement ->
|
||||
icons.add(requirement.icon)
|
||||
tooltips.add(requirement.tooltip)
|
||||
}
|
||||
|
||||
return RenderEvolutionData(icons, tooltips)
|
||||
}
|
||||
|
||||
fun renderLevelUpEvolution(levelUpEvolution: LevelUpEvolution): RenderEvolutionData? {
|
||||
val requirementsDataList = levelUpEvolution.requirements.map { requirement ->
|
||||
resolveRequirementData(requirement)
|
||||
}
|
||||
|
||||
val icons = requirementsDataList.mapNotNull { requirementData -> requirementData?.icon }
|
||||
val tooltips = requirementsDataList.mapNotNull { requirementData -> requirementData?.tooltip }
|
||||
|
||||
return RenderEvolutionData(icons, tooltips)
|
||||
}
|
||||
|
||||
fun renderTradeEvolution(tradeEvolution: TradeEvolution): RenderEvolutionData? {
|
||||
val icons = mutableListOf<Item>(CobblemonItems.LINK_CABLE)
|
||||
val tooltips = mutableListOf<MutableComponent>()
|
||||
|
||||
val species = tradeEvolution.requiredContext.species
|
||||
if (species == null) {
|
||||
tooltips.add(lang("ui.pokedex.evolution.trade.tooltip.with_any_species"))
|
||||
} else {
|
||||
PokemonSpecies.getByIdentifier(cobblemonResource(species))?.let {
|
||||
tooltips.add(lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()))
|
||||
}
|
||||
}
|
||||
|
||||
val requirementsDataList = tradeEvolution.requirements.mapNotNull { requirement ->
|
||||
resolveRequirementData(requirement)
|
||||
}
|
||||
|
||||
requirementsDataList.forEach { requirement ->
|
||||
icons.add(requirement.icon)
|
||||
tooltips.add(requirement.tooltip)
|
||||
}
|
||||
|
||||
return RenderEvolutionData(icons, tooltips)
|
||||
}
|
||||
+379
@@ -0,0 +1,379 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
|
||||
import com.cobblemon.mod.common.api.conditional.RegistryLikeTagCondition
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
|
||||
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.text.add
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.types.ElementalTypes
|
||||
import com.cobblemon.mod.common.pokemon.requirements.*
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.Minecraft
|
||||
import net.minecraft.core.registries.BuiltInRegistries
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.item.Items
|
||||
import net.minecraft.world.level.biome.Biome
|
||||
import net.minecraft.world.level.levelgen.structure.Structure
|
||||
|
||||
data class RenderRequirementData(
|
||||
val icon: Item,
|
||||
val tooltip: MutableComponent,
|
||||
)
|
||||
|
||||
fun renderRequirementData(requirement: Requirement): RenderRequirementData? {
|
||||
when (requirement) {
|
||||
is AdvancementRequirement -> return renderAdvancementRequirement(requirement)
|
||||
is AnyRequirement -> return renderAnyRequirement(requirement)
|
||||
is AreaRequirement -> return renderAreaRequirement(requirement)
|
||||
is AttackDefenceRatioRequirement -> return renderAttackDefenceRatioRequirement(requirement)
|
||||
is BattleCriticalHitsRequirement -> return renderBattleCriticalHitsRequirement(requirement)
|
||||
is BiomeRequirement -> return renderBiomeRequirement(requirement)
|
||||
is BlocksTraveledRequirement -> return renderBlocksTraveledRequirement(requirement)
|
||||
is DamageTakenRequirement -> return renderDamageTakenRequirement(requirement)
|
||||
is DefeatRequirement -> return renderDefeatRequirement(requirement)
|
||||
is FriendshipRequirement -> return renderFriendshipRequirement(requirement)
|
||||
is HeldItemRequirement -> return renderHeldItemRequirement(requirement)
|
||||
is LevelRequirement -> return renderLevelRequirement(requirement)
|
||||
is MoonPhaseRequirement -> return renderMoonPhaseRequirement(requirement)
|
||||
is MoveSetRequirement -> return renderMoveSetRequirement(requirement)
|
||||
is MoveTypeRequirement -> return renderMoveTypeRequirement(requirement)
|
||||
is OwnerHoldsItemRequirement -> return renderOwnerHoldsItemRequirement(requirement)
|
||||
is PartyMemberRequirement -> return renderPartyMemberRequirement(requirement)
|
||||
is PokemonPropertiesRequirement -> return renderPokemonPropertiesRequirement(requirement)
|
||||
is PropertyRangeRequirement -> return renderPropertyRangeRequirement(requirement)
|
||||
is RecoilRequirement -> return renderRecoilRequirement(requirement)
|
||||
is StatCompareRequirement -> return renderStatCompareRequirement(requirement)
|
||||
is StatEqualRequirement -> return renderStatEqualRequirement(requirement)
|
||||
is StructureRequirement -> return renderStructureRequirement(requirement)
|
||||
is TimeRangeRequirement -> return renderTimeRangeRequirement(requirement)
|
||||
is UseMoveRequirement -> return renderUseMoveRequirement(requirement)
|
||||
is WeatherRequirement -> return renderWeatherRequirement(requirement)
|
||||
is WorldRequirement -> return renderWorldRequirement(requirement)
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun renderAdvancementRequirement(advancementRequirement: AdvancementRequirement): RenderRequirementData? {
|
||||
val icon = Items.BOOK
|
||||
val identifier = advancementRequirement.requiredAdvancement
|
||||
val advancementLanguageKey = "${identifier.namespace}.${identifier.path.substringAfterLast('/')}"
|
||||
val advancementComponent = Component.translatable("advancements.$advancementLanguageKey").green()
|
||||
val tooltip = lang("ui.pokedex.requirement.advancement.tooltip", advancementComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderAnyRequirement(anyRequirement: AnyRequirement): RenderRequirementData? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun renderAreaRequirement(areaRequirement: AreaRequirement): RenderRequirementData? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun renderAttackDefenceRatioRequirement(attackDefenceRatioRequirement: AttackDefenceRatioRequirement): RenderRequirementData? {
|
||||
return null
|
||||
}
|
||||
|
||||
fun renderBattleCriticalHitsRequirement(battleCriticalHitsRequirement: BattleCriticalHitsRequirement): RenderRequirementData? {
|
||||
val icon = CobblemonItems.MEDICINAL_LEEK
|
||||
val criticalsComponent = Component.literal(battleCriticalHitsRequirement.amount.toString()).green()
|
||||
val tooltip = lang("ui.pokedex.requirement.battle_critical_hits.tooltip", criticalsComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderBiomeRequirement(biomeRequirement: BiomeRequirement): RenderRequirementData? {
|
||||
val condition = biomeRequirement.biomeCondition
|
||||
val anticondition = biomeRequirement.biomeAnticondition
|
||||
if (condition == null && anticondition == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val icon = Items.COMPASS
|
||||
val displayName: (RegistryLikeCondition<Biome>) -> MutableComponent = {
|
||||
when (it) {
|
||||
is RegistryLikeTagCondition -> Component.literal(it.tag.location().path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) })
|
||||
is RegistryLikeIdentifierCondition -> Component.translatable("biome.${it.identifier.namespace}.${it.identifier.path}")
|
||||
else -> Component.literal(it.toString())
|
||||
}
|
||||
}
|
||||
val tooltip = if (condition != null && anticondition != null) {
|
||||
lang("ui.pokedex.requirement.biome.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
|
||||
} else if (condition != null) {
|
||||
lang("ui.pokedex.requirement.biome.tooltip", displayName(condition).green())
|
||||
} else {
|
||||
lang("ui.pokedex.requirement.biome.anticondition.tooltip", displayName(anticondition!!).green())
|
||||
}
|
||||
|
||||
val playerBiome = Minecraft.getInstance()?.player?.let { it.level().getBiome(it.blockPosition()) }
|
||||
if (playerBiome != null) {
|
||||
val isMet = (condition == null || condition.fits(playerBiome)) && (anticondition == null || !anticondition.fits(playerBiome))
|
||||
tooltip.add(if (isMet) Component.literal(" ✔").green() else Component.literal(" ✘").red())
|
||||
}
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderBlocksTraveledRequirement(blocksTraveledRequirement: BlocksTraveledRequirement): RenderRequirementData? {
|
||||
val icon = Items.LEATHER_BOOTS
|
||||
val blocksComponent = Component.literal(blocksTraveledRequirement.amount.toString()).green()
|
||||
val tooltip = lang("ui.pokedex.requirement.blocks_traveled.tooltip", blocksComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderDamageTakenRequirement(damageTakenRequirement: DamageTakenRequirement): RenderRequirementData? {
|
||||
val icon = CobblemonItems.PUNCHING_GLOVE
|
||||
val blocksComponent = Component.literal(damageTakenRequirement.amount.toString()).green()
|
||||
val tooltip = lang("ui.pokedex.requirement.damage_taken.tooltip", blocksComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderDefeatRequirement(defeatRequirement: DefeatRequirement): RenderRequirementData? {
|
||||
val description = describePokemonProperties(defeatRequirement.target) ?: return null
|
||||
val icon = CobblemonItems.PUNCHING_GLOVE
|
||||
val defeatedAmount = defeatRequirement.amount
|
||||
|
||||
val descriptionComponent = Component.literal(defeatedAmount.toString()).green().add(" ").add(description)
|
||||
val tooltip = lang("ui.pokedex.requirement.defeat.tooltip", descriptionComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderFriendshipRequirement(friendshipRequirement: FriendshipRequirement): RenderRequirementData? {
|
||||
val icon = CobblemonItems.SOOTHE_BELL
|
||||
val friendshipAmountComponent = Component.literal(friendshipRequirement.amount.toString()).green()
|
||||
val tooltip = lang("ui.pokedex.requirement.friendship.tooltip", friendshipAmountComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderHeldItemRequirement(heldItemRequirement: HeldItemRequirement): RenderRequirementData? {
|
||||
val item = heldItemRequirement.itemCondition.items.get().first().value()
|
||||
val tooltip = lang("ui.pokedex.requirement.held_item.tooltip", item.description.plainCopy().green())
|
||||
|
||||
return RenderRequirementData(item, tooltip)
|
||||
}
|
||||
|
||||
fun renderLevelRequirement(levelRequirement: LevelRequirement): RenderRequirementData? {
|
||||
val minLevel = levelRequirement.minLevel
|
||||
val maxLevel = levelRequirement.maxLevel
|
||||
|
||||
val icon = CobblemonItems.RARE_CANDY
|
||||
val tooltip =
|
||||
if (minLevel == maxLevel || maxLevel == Int.MAX_VALUE)
|
||||
lang("ui.pokedex.requirement.level.tooltip.min", Component.literal(minLevel.toString()).green())
|
||||
else
|
||||
lang("ui.pokedex.requirement.level.tooltip.between", Component.literal(minLevel.toString()).green(), Component.literal(maxLevel.toString()).green())
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): RenderRequirementData? {
|
||||
val icon = Items.CLOCK
|
||||
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
|
||||
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderMoveSetRequirement(moveSetRequirement: MoveSetRequirement): RenderRequirementData? {
|
||||
val icon = CobblemonItems.TECHNICAL_MACHINE
|
||||
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): RenderRequirementData? {
|
||||
val type = ElementalTypes.get(moveTypeRequirement.type.name)
|
||||
val icon = when (type) {
|
||||
ElementalTypes.NORMAL -> CobblemonItems.NORMAL_GEM
|
||||
ElementalTypes.FIRE -> CobblemonItems.FIRE_GEM
|
||||
ElementalTypes.WATER -> CobblemonItems.WATER_GEM
|
||||
ElementalTypes.GRASS -> CobblemonItems.GRASS_GEM
|
||||
ElementalTypes.ELECTRIC -> CobblemonItems.ELECTRIC_GEM
|
||||
ElementalTypes.ICE -> CobblemonItems.ICE_GEM
|
||||
ElementalTypes.FIGHTING -> CobblemonItems.FIGHTING_GEM
|
||||
ElementalTypes.POISON -> CobblemonItems.POISON_GEM
|
||||
ElementalTypes.GROUND -> CobblemonItems.GROUND_GEM
|
||||
ElementalTypes.FLYING -> CobblemonItems.FLYING_GEM
|
||||
ElementalTypes.PSYCHIC -> CobblemonItems.PSYCHIC_GEM
|
||||
ElementalTypes.BUG -> CobblemonItems.BUG_GEM
|
||||
ElementalTypes.ROCK -> CobblemonItems.ROCK_GEM
|
||||
ElementalTypes.GHOST -> CobblemonItems.GHOST_GEM
|
||||
ElementalTypes.DRAGON -> CobblemonItems.DRAGON_GEM
|
||||
ElementalTypes.DARK -> CobblemonItems.DARK_GEM
|
||||
ElementalTypes.STEEL -> CobblemonItems.STEEL_GEM
|
||||
ElementalTypes.FAIRY -> CobblemonItems.FAIRY_GEM
|
||||
else -> CobblemonItems.NORMAL_GEM
|
||||
}
|
||||
|
||||
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderOwnerHoldsItemRequirement(ownerHoldsItemRequirement: OwnerHoldsItemRequirement): RenderRequirementData? {
|
||||
val item = ownerHoldsItemRequirement.itemCondition.items.get().first().value()
|
||||
val tooltip = lang("ui.pokedex.requirement.owner_held_item.tooltip", item.description.plainCopy().green())
|
||||
|
||||
return RenderRequirementData(item, tooltip)
|
||||
}
|
||||
|
||||
fun renderPartyMemberRequirement(partyMemberRequirement: PartyMemberRequirement): RenderRequirementData? {
|
||||
val description = describePokemonProperties(partyMemberRequirement.target) ?: return null
|
||||
val icon = CobblemonItems.POKE_BALL
|
||||
val tooltip = if (partyMemberRequirement.contains) {
|
||||
lang("ui.pokedex.requirement.party_member.tooltip", description)
|
||||
} else {
|
||||
lang("ui.pokedex.requirement.party_member.anticondition.tooltip", description)
|
||||
}
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderPokemonPropertiesRequirement(pokemonPropertiesRequirement: PokemonPropertiesRequirement): RenderRequirementData? {
|
||||
val description = describePokemonProperties(pokemonPropertiesRequirement.target) ?: return null
|
||||
val icon = Items.NAME_TAG
|
||||
val tooltip = lang("ui.pokedex.requirement.pokemon_properties.tooltip", description)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderPropertyRangeRequirement(propertyRangeRequirement: PropertyRangeRequirement): RenderRequirementData? {
|
||||
val provider = SpeciesFeatures.getFeature(propertyRangeRequirement.feature) as? IntSpeciesFeatureProvider ?: return null
|
||||
val displayName = provider.display?.name?.takeIf { it.isNotBlank() }?.asTranslated()?.green() ?: return null
|
||||
val icon = provider.itemPoints.keys.firstOrNull()?.let { BuiltInRegistries.ITEM.get(it) } ?: Items.NAME_TAG
|
||||
|
||||
val range = propertyRangeRequirement.range
|
||||
val tooltip = lang(
|
||||
"ui.pokedex.requirement.property_range.tooltip",
|
||||
displayName,
|
||||
Component.literal(range.first.toString()).green(),
|
||||
Component.literal(range.last.toString()).green()
|
||||
)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderRecoilRequirement(recoilRequirement: RecoilRequirement): RenderRequirementData? {
|
||||
val icon = CobblemonItems.LIFE_ORB
|
||||
val recoilComponent = Component.literal(recoilRequirement.amount.toString()).green()
|
||||
val tooltip = lang("ui.pokedex.requirement.recoil.tooltip", recoilComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderStatCompareRequirement(statCompareRequirement: StatCompareRequirement): RenderRequirementData? {
|
||||
val highStat = Stats.getStat(statCompareRequirement.highStat)
|
||||
val lowStat = Stats.getStat(statCompareRequirement.lowStat)
|
||||
|
||||
val icon = when (highStat) {
|
||||
Stats.HP, Stats.EVASION, Stats.ACCURACY -> CobblemonItems.HEALTH_MOCHI
|
||||
Stats.ATTACK -> CobblemonItems.MUSCLE_MOCHI
|
||||
Stats.DEFENCE -> CobblemonItems.RESIST_MOCHI
|
||||
Stats.SPECIAL_ATTACK -> CobblemonItems.GENIUS_MOCHI
|
||||
Stats.SPECIAL_DEFENCE -> CobblemonItems.CLEVER_MOCHI
|
||||
Stats.SPEED -> CobblemonItems.SWIFT_MOCHI
|
||||
}
|
||||
|
||||
val highStatComponent = highStat.displayName.plainCopy().green()
|
||||
val lowStatComponent = lowStat.displayName.plainCopy().green()
|
||||
val tooltip = lang("ui.pokedex.requirement.stat_compare.tooltip", highStatComponent, lowStatComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderStatEqualRequirement(statEqualRequirement: StatEqualRequirement): RenderRequirementData? {
|
||||
val statOne = Stats.getStat(statEqualRequirement.statOne)
|
||||
val statTwo = Stats.getStat(statEqualRequirement.statTwo)
|
||||
|
||||
val statOneComponent = statOne.displayName.plainCopy().green()
|
||||
val statTwoComponent = statTwo.displayName.plainCopy().green()
|
||||
|
||||
val icon = CobblemonItems.FRESH_START_MOCHI
|
||||
val tooltip = lang("ui.pokedex.requirement.stat_equal.tooltip", statOneComponent, statTwoComponent)
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderStructureRequirement(structureRequirement: StructureRequirement): RenderRequirementData? {
|
||||
val condition = structureRequirement.structureCondition
|
||||
val anticondition = structureRequirement.structureAnticondition
|
||||
if (condition == null && anticondition == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
val icon = Items.STRUCTURE_BLOCK
|
||||
val displayName: (RegistryLikeCondition<Structure>) -> MutableComponent = {
|
||||
val path = when (it) {
|
||||
is RegistryLikeTagCondition -> it.tag.location().path
|
||||
is RegistryLikeIdentifierCondition -> it.identifier.path
|
||||
else -> it.toString()
|
||||
}
|
||||
Component.literal(path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) })
|
||||
}
|
||||
val tooltip = if (condition != null && anticondition != null) {
|
||||
lang("ui.pokedex.requirement.structure.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
|
||||
} else if (condition != null) {
|
||||
lang("ui.pokedex.requirement.structure.tooltip", displayName(condition).green())
|
||||
} else {
|
||||
lang("ui.pokedex.requirement.structure.anticondition.tooltip", displayName(anticondition!!).green())
|
||||
}
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): RenderRequirementData? {
|
||||
val icon = Items.CLOCK
|
||||
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
|
||||
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderUseMoveRequirement(useMoveRequirement: UseMoveRequirement): RenderRequirementData? {
|
||||
val icon = CobblemonItems.CHOICE_BAND
|
||||
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderWeatherRequirement(weatherRequirement: WeatherRequirement): RenderRequirementData? {
|
||||
val icon: Item
|
||||
val tooltip: MutableComponent
|
||||
|
||||
if (weatherRequirement.isThundering == true) {
|
||||
icon = Items.LIGHTNING_ROD
|
||||
tooltip = lang("ui.pokedex.requirement.weather.tooltip.thundering")
|
||||
} else {
|
||||
icon = Items.WATER_BUCKET
|
||||
tooltip = lang("ui.pokedex.requirement.weather.tooltip.raining")
|
||||
}
|
||||
|
||||
return RenderRequirementData(icon, tooltip)
|
||||
}
|
||||
|
||||
fun renderWorldRequirement(worldRequirement: WorldRequirement): RenderRequirementData? {
|
||||
return null
|
||||
}
|
||||
+571
@@ -0,0 +1,571 @@
|
||||
/*
|
||||
* Copyright (C) 2023 Cobblemon Contributors
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.api.gui.*
|
||||
import com.cobblemon.mod.common.api.pokedex.Dexes
|
||||
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
|
||||
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
|
||||
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
|
||||
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
|
||||
import com.cobblemon.mod.common.api.text.add
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.CobblemonClient
|
||||
import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_DESCRIPTION_HEIGHT
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_SLOT_SIZE
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
|
||||
import com.cobblemon.mod.common.client.gui.pokedex.widgets.PokemonSlotWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
|
||||
import com.cobblemon.mod.common.client.render.drawScaledText
|
||||
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
|
||||
import com.cobblemon.mod.common.pokemon.Gender
|
||||
import com.cobblemon.mod.common.pokemon.RenderablePokemon
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.lang
|
||||
import net.minecraft.client.gui.GuiGraphics
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.network.chat.MutableComponent
|
||||
|
||||
class EvolutionTreeWidget(
|
||||
val parent: PokedexGUI,
|
||||
val pX: Int,
|
||||
val pY: Int,
|
||||
) : SoundlessWidget(
|
||||
pX, pY,
|
||||
HALF_OVERLAY_WIDTH,
|
||||
POKEMON_DESCRIPTION_HEIGHT,
|
||||
lang("ui.pokedex.pokemon_info")
|
||||
) {
|
||||
companion object {
|
||||
private val slotResource = cobblemonResource("textures/gui/pokedex/pokedex_slot.png")
|
||||
private val expandResource = cobblemonResource("textures/gui/pokedex/expand.png")
|
||||
private val minimizeResource = cobblemonResource("textures/gui/pokedex/minimize.png")
|
||||
|
||||
const val X_MARGIN = 180
|
||||
const val Y_MARGIN_MINIMIZED = 135
|
||||
const val Y_MARGIN_EXPANDED = 53
|
||||
const val EXPANDED_HEIGHT = 125
|
||||
|
||||
const val HORIZONTAL_PADDING = 38
|
||||
const val VERTICAL_PADDING = 2
|
||||
const val TREE_FORK_LENGTH = 4
|
||||
|
||||
const val ICON_X_PADDING = -4.0
|
||||
const val ICON_Y_PADDING = -4.0
|
||||
const val MILLIS_BETWEEN_ICONS_CYCLE = 1000
|
||||
|
||||
const val ICON_SLOT_BACKGROUND_SIZE = 8
|
||||
const val ICON_SLOT_PADDING = 4
|
||||
const val ICON_SLOT_HALF_PADDING = ICON_SLOT_PADDING / 2
|
||||
|
||||
const val LINE_COLOR = 0xff1d6b85.toInt()
|
||||
|
||||
const val EXPAND_BUTTON_X_MARGIN = 127f
|
||||
const val EXPAND_BUTTON_Y_MARGIN = -10.5f
|
||||
const val EXPAND_BUTTON_SIZE = 20
|
||||
|
||||
private var previousRootPokedexEntry: PokedexEntry? = null
|
||||
private var previousRootPokedexForm: PokedexForm? = null
|
||||
}
|
||||
|
||||
private var rootTreeDrawingNode: TreeDrawingNode<EvolutionNode>? = null
|
||||
private var treeDrawingBounds: TreeDrawingBounds? = null
|
||||
private var zoomLevel: Double = 0.0
|
||||
private var panningX: Double = 0.0
|
||||
private var panningY: Double = 0.0
|
||||
|
||||
private var hoveredTooltips: List<MutableComponent>? = null
|
||||
|
||||
private val expandButton: ScaledButton = ScaledButton(
|
||||
0f,
|
||||
0f,
|
||||
EXPAND_BUTTON_SIZE,
|
||||
EXPAND_BUTTON_SIZE,
|
||||
resource = expandResource,
|
||||
clickAction = {
|
||||
parent.evolutionTreeExpanded = !parent.evolutionTreeExpanded
|
||||
expandButton.resource = if (parent.evolutionTreeExpanded) minimizeResource else expandResource
|
||||
updatePositioning()
|
||||
}
|
||||
).apply { addWidget(this) }
|
||||
|
||||
override fun renderWidget(
|
||||
guiGraphics: GuiGraphics,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
partialTick: Float,
|
||||
) {
|
||||
drawScaledText(
|
||||
context = guiGraphics,
|
||||
font = CobblemonResources.DEFAULT_LARGE,
|
||||
text = Component.translatable("cobblemon.ui.pokedex.info.evolution_tree").bold(),
|
||||
x = x + 9,
|
||||
y = y - 10,
|
||||
shadow = true
|
||||
)
|
||||
|
||||
expandButton.render(guiGraphics, mouseX, mouseY, partialTick)
|
||||
|
||||
rootTreeDrawingNode?.let {
|
||||
enableScissors(guiGraphics)
|
||||
renderTreeDrawingNode(guiGraphics, it, mouseX, mouseY, partialTick)
|
||||
guiGraphics.disableScissor()
|
||||
|
||||
hoveredTooltips?.let { tooltips ->
|
||||
renderTooltip(
|
||||
guiGraphics,
|
||||
tooltips,
|
||||
mouseX,
|
||||
mouseY,
|
||||
partialTick,
|
||||
4,
|
||||
)
|
||||
}
|
||||
|
||||
hoveredTooltips = null
|
||||
}
|
||||
}
|
||||
|
||||
fun setup(pokedexEntry: PokedexEntry?, pokedexForm: PokedexForm?) {
|
||||
if (pokedexEntry == null || pokedexForm == null) return
|
||||
|
||||
updatePositioning()
|
||||
|
||||
val (rootPokedexEntry, rootPokedexForm) = Dexes.findRootDexEntryBySpeciesId(pokedexEntry.speciesId, pokedexForm.displayForm)
|
||||
val rootEvolutionNode = buildEvolutionTree(rootPokedexEntry, rootPokedexForm, null)
|
||||
|
||||
// val isSameEntry = previousRootPokedexEntry != null && pokedexEntry?.id == previousRootPokedexEntry?.id
|
||||
// val isSameForm =
|
||||
// previousRootPokedexForm != null && pokedexForm?.displayForm == previousRootPokedexForm?.displayForm
|
||||
|
||||
// TODO: figure out how to prevent pokedex from resetting all the widgets state
|
||||
// if (isSameEntry && isSameForm) {
|
||||
// return
|
||||
// }
|
||||
|
||||
previousRootPokedexEntry = pokedexEntry
|
||||
previousRootPokedexForm = pokedexForm
|
||||
|
||||
createDrawingTree(rootEvolutionNode).also {
|
||||
rootTreeDrawingNode = it
|
||||
treeDrawingBounds = calculateBounds<EvolutionNode>(it)
|
||||
}
|
||||
|
||||
zoomLevel = 0.0
|
||||
panningX = 0.0
|
||||
panningY = 0.0
|
||||
}
|
||||
|
||||
fun updatePositioning() {
|
||||
x = pX + X_MARGIN
|
||||
y = pY + if (parent.evolutionTreeExpanded) Y_MARGIN_EXPANDED else Y_MARGIN_MINIMIZED
|
||||
height = if (parent.evolutionTreeExpanded) EXPANDED_HEIGHT else POKEMON_DESCRIPTION_HEIGHT
|
||||
|
||||
panningX = 0.0
|
||||
panningY = 0.0
|
||||
|
||||
expandButton.buttonX = x + EXPAND_BUTTON_X_MARGIN
|
||||
expandButton.buttonY = y + EXPAND_BUTTON_Y_MARGIN
|
||||
}
|
||||
|
||||
private fun buildEvolutionTree(
|
||||
pokedexEntry: PokedexEntry,
|
||||
pokedexForm: PokedexForm,
|
||||
evolution: Evolution? = null,
|
||||
resultProperties: PokemonProperties? = evolution?.result,
|
||||
renderData: RenderEvolutionData? = evolution?.let { renderEvolutionData(it) }
|
||||
): EvolutionNode {
|
||||
val species = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId)!!
|
||||
|
||||
val formEquivalents = Dexes.getFormEquivalents(pokedexForm.displayForm)
|
||||
val form = species.forms.firstOrNull { formEquivalents.contains(it.name.lowercase()) }
|
||||
val parentSpecies = Dexes.findDexEntryBySpeciesId(species.resourceIdentifier)!!
|
||||
val parentPokedexForm = parentSpecies.forms.first { formEquivalents.contains(it.displayForm.lowercase()) }
|
||||
|
||||
val aspects = resultProperties?.aspects?.toMutableSet() ?: mutableSetOf()
|
||||
form?.aspects?.let { aspects.addAll(it) }
|
||||
|
||||
val discoveryLevel = CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry, pokedexForm, aspects)
|
||||
|
||||
val evolutions = form?.evolutions ?: species.evolutions
|
||||
|
||||
// Avoid evolution.result.create() here: it builds a full battle-ready Pokemon (initializeMovesetFromDefault
|
||||
// etc.) which relies on server-only data like CobblemonMovesetBuilders and crashes on the client
|
||||
val children = evolutions.flatMap { evolution ->
|
||||
val baseRenderData = renderEvolutionData(evolution)
|
||||
|
||||
listOfNotNull(evolution.result, evolution.shedder).mapNotNull { resultProperties ->
|
||||
val resultSpecies = resultProperties.species
|
||||
?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
|
||||
?: return@mapNotNull null
|
||||
|
||||
val resultForm = resultSpecies.getForm(aspects + resultProperties.aspects)
|
||||
|
||||
val speciesId = resultSpecies.resourceIdentifier
|
||||
val childPokedexEntry = Dexes.findDexEntryBySpeciesId(speciesId)
|
||||
|
||||
if (childPokedexEntry == null) {
|
||||
Cobblemon.LOGGER.warn("Dex entry not found for species: $speciesId")
|
||||
return@mapNotNull null
|
||||
}
|
||||
|
||||
val formEquivalents = Dexes.getFormEquivalents(resultForm.name.lowercase())
|
||||
val childPokedexForm = childPokedexEntry.forms.first { formEquivalents.contains(it.displayForm.lowercase()) }
|
||||
|
||||
val childRenderData = if (resultProperties === evolution.shedder) {
|
||||
RenderEvolutionData(
|
||||
icons = (baseRenderData?.icons ?: emptyList()) + CobblemonItems.POKE_BALL,
|
||||
tooltips = (baseRenderData?.tooltips ?: emptyList()) + lang("ui.pokedex.evolution.shedder.tooltip"),
|
||||
)
|
||||
} else {
|
||||
baseRenderData
|
||||
}
|
||||
|
||||
return@mapNotNull buildEvolutionTree(childPokedexEntry, childPokedexForm, evolution, resultProperties, childRenderData)
|
||||
}
|
||||
}
|
||||
|
||||
return EvolutionNode(pokedexEntry, parentPokedexForm, aspects, discoveryLevel, children, renderData)
|
||||
}
|
||||
|
||||
private fun renderTreeDrawingNode(
|
||||
guiGraphics: GuiGraphics,
|
||||
treeDrawingNode: TreeDrawingNode<EvolutionNode>,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
delta: Float,
|
||||
) {
|
||||
val (treeX, treeY) = getDrawingCoordinates(treeDrawingNode)
|
||||
|
||||
if (treeDrawingNode.children.size == 1) {
|
||||
val child = treeDrawingNode.children.first()
|
||||
val (childX, _) = getDrawingCoordinates(child)
|
||||
|
||||
val lineStartX = treeX + SCROLL_SLOT_SIZE
|
||||
val lineEndX = childX
|
||||
val lineY = treeY + SCROLL_SLOT_SIZE / 2
|
||||
|
||||
child.value.renderData?.let { renderData ->
|
||||
drawEvolutionInfo(
|
||||
guiGraphics,
|
||||
renderData,
|
||||
lineStartX,
|
||||
lineEndX,
|
||||
lineY,
|
||||
mouseX,
|
||||
mouseY,
|
||||
delta,
|
||||
)
|
||||
}
|
||||
} else if (treeDrawingNode.children.size > 1) {
|
||||
val treeForkStart = treeX + SCROLL_SLOT_SIZE
|
||||
val treeForkEnd = treeX + SCROLL_SLOT_SIZE + TREE_FORK_LENGTH
|
||||
val middleStart = treeY + SCROLL_SLOT_SIZE / 2
|
||||
|
||||
val (_, firstChildY) = getDrawingCoordinates(treeDrawingNode.children.first())
|
||||
val (_, lastChildY) = getDrawingCoordinates(treeDrawingNode.children.last())
|
||||
|
||||
guiGraphics.hLine(
|
||||
treeForkStart,
|
||||
treeForkEnd,
|
||||
middleStart,
|
||||
LINE_COLOR,
|
||||
)
|
||||
|
||||
guiGraphics.vLine(
|
||||
treeForkEnd,
|
||||
firstChildY + SCROLL_SLOT_SIZE / 2,
|
||||
lastChildY + SCROLL_SLOT_SIZE / 2,
|
||||
LINE_COLOR,
|
||||
)
|
||||
|
||||
treeDrawingNode.children.forEach { child ->
|
||||
val (childX, childY) = getDrawingCoordinates(child)
|
||||
|
||||
val lineStartX = treeForkEnd
|
||||
val lineEndX = childX
|
||||
val lineY = childY + SCROLL_SLOT_SIZE / 2
|
||||
|
||||
child.value.renderData?.let { renderData ->
|
||||
drawEvolutionInfo(
|
||||
guiGraphics,
|
||||
renderData,
|
||||
lineStartX,
|
||||
lineEndX,
|
||||
lineY,
|
||||
mouseX,
|
||||
mouseY,
|
||||
delta,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
treeDrawingNode.children.forEach { child ->
|
||||
renderTreeDrawingNode(guiGraphics, child, mouseX, mouseY, delta)
|
||||
}
|
||||
|
||||
val isVisible =
|
||||
treeX >= x - SCROLL_SLOT_SIZE &&
|
||||
treeX <= x + width &&
|
||||
treeY >= y - SCROLL_SLOT_SIZE &&
|
||||
treeY <= y + height
|
||||
|
||||
if (!isVisible) return
|
||||
|
||||
val evolutionNode = treeDrawingNode.value
|
||||
val isSlotHovered =
|
||||
isElementHovered(mouseX, mouseY, treeX, treeX + SCROLL_SLOT_SIZE, treeY, treeY + SCROLL_SLOT_SIZE)
|
||||
|
||||
val discoveryLevel = evolutionNode.pokedexEntryProgress
|
||||
if (isSlotHovered) {
|
||||
val text =
|
||||
if (discoveryLevel == PokedexEntryProgress.UNREGISTERED) {
|
||||
lang("ui.pokedex.unknown")
|
||||
} else {
|
||||
val species = PokemonSpecies.getByIdentifier(evolutionNode.pokedexEntry.speciesId)!!
|
||||
val speciesComponent = species.translatedName
|
||||
val formName = evolutionNode.pokedexForm.displayForm.lowercase()
|
||||
val formComponent =
|
||||
if (formName == "normal") null
|
||||
else lang("ui.pokedex.info.form.${species}-${formName.replace("-", "")}")
|
||||
|
||||
formComponent?.add(" ")?.add(speciesComponent) ?: speciesComponent
|
||||
}
|
||||
|
||||
guiGraphics.disableScissor()
|
||||
renderTooltip(
|
||||
context = guiGraphics,
|
||||
text = text,
|
||||
posX = mouseX,
|
||||
posY = mouseY,
|
||||
delta,
|
||||
4,
|
||||
)
|
||||
enableScissors(guiGraphics)
|
||||
}
|
||||
|
||||
|
||||
val pokedexEntry = evolutionNode.pokedexEntry
|
||||
val pokedexForm = evolutionNode.pokedexForm
|
||||
val species = PokemonSpecies.getByIdentifier(evolutionNode.pokedexEntry.speciesId)!!
|
||||
val encounteredForms = CobblemonClient.clientPokedexData.getEncounteredForms(pokedexEntry)
|
||||
val formToRender = encounteredForms.firstOrNull { it.displayForm.equals(pokedexForm.displayForm, ignoreCase = true) }
|
||||
|
||||
val renderablePokemon =
|
||||
if (formToRender != null) {
|
||||
val seenGenders = CobblemonClient.clientPokedexData.getSeenGenders(pokedexEntry, formToRender)
|
||||
val seenShinyStates = CobblemonClient.clientPokedexData.getSeenShinyStates(pokedexEntry, formToRender)
|
||||
|
||||
val aspectsToDraw = (pokedexEntry.displayAspects + evolutionNode.aspects).toMutableSet()
|
||||
|
||||
val formAspects = species.forms.firstOrNull { it.name.equals(formToRender.displayForm, ignoreCase = true) }?.aspects
|
||||
if (formAspects != null) {
|
||||
aspectsToDraw.addAll(formAspects)
|
||||
}
|
||||
|
||||
if (seenGenders.contains(parent.gender)) {
|
||||
aspectsToDraw.add(parent.gender.name.lowercase())
|
||||
} else {
|
||||
aspectsToDraw.add((seenGenders.firstOrNull() ?: Gender.GENDERLESS).name.lowercase())
|
||||
}
|
||||
|
||||
if (parent.shiny && seenShinyStates.contains("shiny")) {
|
||||
aspectsToDraw.add("shiny")
|
||||
}
|
||||
|
||||
RenderablePokemon(species, aspectsToDraw)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
PokemonSlotWidget.render(
|
||||
context = guiGraphics,
|
||||
x = treeX,
|
||||
y = treeY,
|
||||
isHovered = isSlotHovered,
|
||||
species = species,
|
||||
renderablePokemon = renderablePokemon,
|
||||
discoveryLevel = discoveryLevel,
|
||||
)
|
||||
}
|
||||
|
||||
private fun drawEvolutionInfo(
|
||||
guiGraphics: GuiGraphics,
|
||||
renderData: RenderEvolutionData,
|
||||
lineStartX: Int,
|
||||
lineEndX: Int,
|
||||
lineY: Int,
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
delta: Float,
|
||||
) {
|
||||
val lineXMiddle = (lineStartX + lineEndX) / 2
|
||||
val iconX = (lineXMiddle + ICON_X_PADDING - ICON_SLOT_HALF_PADDING).toInt()
|
||||
val iconY = (lineY + ICON_Y_PADDING - ICON_SLOT_HALF_PADDING).toInt()
|
||||
val iconSize = ICON_SLOT_BACKGROUND_SIZE + ICON_SLOT_PADDING
|
||||
|
||||
val isVisible =
|
||||
iconX >= x - iconSize &&
|
||||
iconX <= x + width &&
|
||||
iconY >= y - iconSize &&
|
||||
iconY <= y + height
|
||||
|
||||
if (!isVisible) {
|
||||
return
|
||||
}
|
||||
|
||||
if (renderData.icons.isEmpty()) {
|
||||
guiGraphics.hLine(
|
||||
lineStartX,
|
||||
lineEndX,
|
||||
lineY,
|
||||
LINE_COLOR,
|
||||
)
|
||||
} else {
|
||||
guiGraphics.hLine(
|
||||
lineStartX,
|
||||
iconX - 1,
|
||||
lineY,
|
||||
LINE_COLOR,
|
||||
)
|
||||
|
||||
guiGraphics.hLine(
|
||||
iconX + iconSize,
|
||||
lineEndX,
|
||||
lineY,
|
||||
LINE_COLOR,
|
||||
)
|
||||
|
||||
blitk(
|
||||
matrixStack = guiGraphics.pose(),
|
||||
texture = slotResource,
|
||||
x = iconX,
|
||||
y = iconY,
|
||||
width = iconSize,
|
||||
height = iconSize,
|
||||
)
|
||||
|
||||
val index = (System.currentTimeMillis() / MILLIS_BETWEEN_ICONS_CYCLE) % renderData.icons.size
|
||||
val item = renderData.icons[index.toInt()]
|
||||
|
||||
renderScaledGuiItemIcon(
|
||||
itemStack = item.defaultInstance,
|
||||
x = lineXMiddle + ICON_X_PADDING,
|
||||
y = lineY + ICON_Y_PADDING,
|
||||
matrixStack = guiGraphics.pose(),
|
||||
scale = PokedexGUIConstants.SCALE.toDouble(),
|
||||
)
|
||||
}
|
||||
|
||||
val isLineHovered = isElementHovered(mouseX, mouseY, lineStartX, lineEndX, lineY - 1, lineY + 1)
|
||||
val isIconHovered = isElementHovered(
|
||||
mouseX,
|
||||
mouseY,
|
||||
iconX.toInt(),
|
||||
iconX.toInt() + iconSize,
|
||||
iconY.toInt(),
|
||||
iconY.toInt() + iconSize,
|
||||
)
|
||||
|
||||
if (renderData.tooltips.isNotEmpty() &&
|
||||
(isLineHovered || isIconHovered) &&
|
||||
this.isHovered(mouseX.toDouble(), mouseY.toDouble())
|
||||
) {
|
||||
hoveredTooltips = renderData.tooltips
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDrawingCoordinates(node: TreeDrawingNode<EvolutionNode>): Pair<Int, Int> {
|
||||
val halfSlot = SCROLL_SLOT_SIZE / 2f
|
||||
|
||||
val xStep = halfSlot + HORIZONTAL_PADDING
|
||||
val yStep = halfSlot + VERTICAL_PADDING
|
||||
|
||||
// Swaps the x and y to draw the tree horizontally
|
||||
val xDislocation = (node.y - treeDrawingBounds!!.yCenter) * xStep
|
||||
val yDislocation = (node.x - treeDrawingBounds!!.xCenter) * yStep
|
||||
|
||||
val xCenter = width / 2f - halfSlot
|
||||
val yCenter = height / 2f - halfSlot
|
||||
|
||||
val screenX = (xCenter.toInt() + xDislocation.toInt() + panningX.toInt() + x)
|
||||
val screenY = (yCenter.toInt() + yDislocation.toInt() + panningY.toInt() + y)
|
||||
|
||||
return screenX to screenY
|
||||
}
|
||||
|
||||
private fun isElementHovered(
|
||||
mouseX: Int,
|
||||
mouseY: Int,
|
||||
startX: Int,
|
||||
endX: Int,
|
||||
startY: Int,
|
||||
endY: Int,
|
||||
): Boolean {
|
||||
return mouseX >= startX && mouseX <= endX && mouseY >= startY && mouseY <= endY
|
||||
}
|
||||
|
||||
override fun mouseDragged(
|
||||
mouseX: Double,
|
||||
mouseY: Double,
|
||||
button: Int,
|
||||
deltaX: Double,
|
||||
deltaY: Double
|
||||
): Boolean {
|
||||
panningX += deltaX
|
||||
panningY += deltaY
|
||||
|
||||
val bounds = treeDrawingBounds ?: return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
val treeHeight = bounds.height.coerceAtLeast(1)
|
||||
val treeWidth = bounds.width.coerceAtLeast(1)
|
||||
|
||||
val xStep = HORIZONTAL_PADDING + SCROLL_SLOT_SIZE / 2.0
|
||||
val yStep = VERTICAL_PADDING + SCROLL_SLOT_SIZE / 2.0
|
||||
|
||||
val minPanningX = (treeHeight - 1) * -xStep / 2.0 - (width + SCROLL_SLOT_SIZE) / 2.0
|
||||
val maxPanningX = (treeHeight - 1) * xStep / 2.0 + (width + SCROLL_SLOT_SIZE) / 2.0
|
||||
|
||||
val divideBy = if (treeWidth % 2 == 0) 4.0 else 2.0
|
||||
val minPanningY = (treeWidth - 1) * -yStep / 2.0 - height / 2.0 + SCROLL_SLOT_SIZE / divideBy
|
||||
val maxPanningY = (treeWidth - 1) * yStep / 2.0 + height / 2.0 - SCROLL_SLOT_SIZE / divideBy
|
||||
|
||||
panningX = panningX.coerceIn(minPanningX, maxPanningX)
|
||||
panningY = panningY.coerceIn(minPanningY, maxPanningY)
|
||||
|
||||
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
|
||||
}
|
||||
|
||||
fun isHovered(mouseX: Double, mouseY: Double): Boolean {
|
||||
return mouseX.toInt() in x..(x + width) && mouseY.toInt() in y..(y + height)
|
||||
}
|
||||
|
||||
fun enableScissors(guiGraphics: GuiGraphics) {
|
||||
guiGraphics.enableScissor(x + 1, y, x + width - 1, y + height)
|
||||
}
|
||||
|
||||
private class EvolutionNode(
|
||||
val pokedexEntry: PokedexEntry,
|
||||
val pokedexForm: PokedexForm,
|
||||
val aspects: Set<String>,
|
||||
val pokedexEntryProgress: PokedexEntryProgress,
|
||||
override val children: List<EvolutionNode>,
|
||||
val renderData: RenderEvolutionData?,
|
||||
) : TreeNode<EvolutionNode>
|
||||
}
|
||||
+4
-6
@@ -8,12 +8,10 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.gui.startselection.widgets
|
||||
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.text.bold
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
|
||||
import com.cobblemon.mod.common.client.gui.VariableObjectSelectionList
|
||||
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
|
||||
import com.cobblemon.mod.common.client.gui.startselection.StarterSelectionScreen
|
||||
@@ -29,12 +27,11 @@ import org.joml.Quaternionf
|
||||
import org.joml.Vector3f
|
||||
|
||||
class CategoryList(
|
||||
private val paneWidth: Int,
|
||||
private val paneHeight: Int,
|
||||
paneWidth: Int,
|
||||
paneHeight: Int,
|
||||
private val categories: List<RenderableStarterCategory>,
|
||||
val listX: Int,
|
||||
val listY: Int,
|
||||
private val minecraft: Minecraft = Minecraft.getInstance(),
|
||||
private val starterSelectionScreen: StarterSelectionScreen
|
||||
) : VariableObjectSelectionList(
|
||||
listX,
|
||||
@@ -70,7 +67,8 @@ class CategoryList(
|
||||
}
|
||||
|
||||
private fun createEntries() = categories.map { category ->
|
||||
val rows = (category.pokemon.size - 1) / 3 + 1
|
||||
val pokemonCount = category.pokemon.size - if (category.randomStarter) 0 else 1
|
||||
val rows = pokemonCount / 3 + 1
|
||||
val height = CONTAINER_BUTTON_SIZE * rows + BUTTON_SEPERATION * (rows + 1) + HEADER_HEIGHT + 3
|
||||
Category(category, height)
|
||||
}
|
||||
|
||||
+4
-1
@@ -21,7 +21,10 @@ object SetActiveTMPacketHandler : ServerNetworkPacketHandler<SetActiveTMPacket>
|
||||
val menu = player.containerMenu as? TMMachineMenu ?: return
|
||||
val tmData = Cobblemon.playerDataManager.getTMData(player)
|
||||
|
||||
if (packet.tm != null && (!packet.tm.isPassivelyObtained() && packet.tm.id !in tmData.learnedTMs)) {
|
||||
if (packet.tm != null
|
||||
&& !Cobblemon.config.unlockAllMoveDexMovesByDefault
|
||||
&& !packet.tm.isPassivelyObtained()
|
||||
&& packet.tm.id !in tmData.learnedTMs) {
|
||||
return // Hacker smh
|
||||
}
|
||||
|
||||
|
||||
+14
-28
@@ -8,9 +8,9 @@
|
||||
|
||||
package com.cobblemon.mod.common.client.render.block
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonItemComponents
|
||||
import com.cobblemon.mod.common.block.entity.DiscShelfBlockEntity
|
||||
import com.cobblemon.mod.common.CobblemonItems
|
||||
import com.cobblemon.mod.common.client.render.color.TechnicalMachineItemColorProvider
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.mojang.blaze3d.vertex.PoseStack
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer
|
||||
@@ -37,15 +37,15 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
|
||||
private val slotHeightPixels = 2
|
||||
private val textureSize = 16f
|
||||
|
||||
private val slotUMax = slotWidthPixels / textureSize
|
||||
private val slotVMax = slotHeightPixels / textureSize
|
||||
|
||||
private val leftMargin = 1f
|
||||
private val rightMargin = 1f
|
||||
private val topMargin = 1f
|
||||
private val interRowSpacing = 0f
|
||||
private val tmOverlayDepthOffset = -0.0002f
|
||||
private val slotBrightnessMultiplier = 0.65f
|
||||
|
||||
private val tmBaseTexture = cobblemonResource("textures/block/disc_shelf/technical_machine_base.png")
|
||||
private val tmOverlayTexture = cobblemonResource("textures/block/disc_shelf/technical_machine_overlay.png")
|
||||
private val musicDiscUnknownTexture = cobblemonResource("textures/block/disc_shelf/music_disc_unknown.png")
|
||||
|
||||
override fun render(
|
||||
@@ -98,35 +98,19 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
|
||||
poseStack.translate(posX, posY, -0.001f)
|
||||
|
||||
if (item.item == CobblemonItems.TECHNICAL_MACHINE) {
|
||||
val baseColor = TechnicalMachineItemColorProvider.getColor(item, 0)
|
||||
val overlayColor = TechnicalMachineItemColorProvider.getColor(item, 1)
|
||||
|
||||
val elementId = item.components.get(CobblemonItemComponents.TM_MOVE)!!.move.elementalType.showdownId
|
||||
val texture = cobblemonResource("textures/block/disc_shelf/${elementId}_tm.png")
|
||||
renderSlotQuad(
|
||||
poseStack,
|
||||
buffer,
|
||||
tmBaseTexture,
|
||||
baseColor,
|
||||
texture,
|
||||
0xFFFFFF,
|
||||
packedLight,
|
||||
slotWidth,
|
||||
slotHeight,
|
||||
tint = true,
|
||||
useTextRenderType = true
|
||||
)
|
||||
|
||||
poseStack.pushPose()
|
||||
poseStack.translate(0.0, 0.0, tmOverlayDepthOffset.toDouble())
|
||||
renderSlotQuad(
|
||||
poseStack,
|
||||
buffer,
|
||||
tmOverlayTexture,
|
||||
overlayColor,
|
||||
packedLight,
|
||||
slotWidth,
|
||||
slotHeight,
|
||||
tint = true,
|
||||
useTextRenderType = true
|
||||
)
|
||||
poseStack.popPose()
|
||||
} else {
|
||||
val tex = getItemTexture(item)
|
||||
renderSlotQuad(
|
||||
@@ -176,13 +160,15 @@ class DiscShelfBlockEntityRenderer(ctx: BlockEntityRendererProvider.Context) :
|
||||
val g = (gBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
|
||||
val b = (bBase * slotBrightnessMultiplier).coerceIn(0f, 1f)
|
||||
|
||||
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(0f, 1f)
|
||||
// The quad faces -Z, so a viewer looking at it sees +X on their left.
|
||||
// U runs from slotUMax at x = 0 to 0 at x = width so the art is not mirrored.
|
||||
consumer.addVertex(matrix, 0f, 0f, 0f).setColor(r, g, b, a).setUv(slotUMax, slotVMax)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
|
||||
consumer.addVertex(matrix, 0f, height, 0f).setColor(r, g, b, a).setUv(slotUMax, 0f)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(1f, 0f)
|
||||
consumer.addVertex(matrix, width, height, 0f).setColor(r, g, b, a).setUv(0f, 0f)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(1f, 1f)
|
||||
consumer.addVertex(matrix, width, 0f, 0f).setColor(r, g, b, a).setUv(0f, slotVMax)
|
||||
.setUv1(0, 0).setUv2(light and 0xFFFF, light shr 16).setNormal(0f, 0f, -1f)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user