Compare commits

...
Author SHA1 Message Date
jmmcp ddfc8194c8 Big license moment 2023-09-24 20:47:22 -04:00
jmmcp 22c5e55a6c Create basis and example injection for custom overworld music. 2023-09-24 19:22:20 -04:00
Veraxiel 96d2b322a3 Update is_dense.json 2023-09-24 03:04:30 +00:00
Veraxiel 7bae99468b Update is_sparse.json 2023-09-24 03:04:26 +00:00
Veraxiel 8b99cfe98d Update is_dense.json 2023-09-24 01:52:15 +00:00
Veraxiel ac6eb38152 Update is_sparse.json 2023-09-24 01:52:12 +00:00
Paul 1621910428 Merge remote-tracking branch 'origin/main' 2023-09-24 11:45:50 +10:00
Paul b74037afb6 Fixed incompatibility crash with Pixelmon Reforged 2023-09-24 11:45:39 +10:00
Veraxiel 753bfa6936 Update is_sparse.json 2023-09-24 01:32:55 +00:00
Hiroku 53d575b1c4 Merge branch 'some-api-utilities' into 'main'
Add some API utilities

See merge request cable-mc/cobblemon!636
2023-09-23 23:57:48 +00:00
Paul 97e90a87c1 Rework density tags to be sparse and dense 2023-09-24 09:13:14 +10:00
Neovitalism a72525cf65 Add some API utilities 2023-09-23 18:38:06 -04:00
Paul 07e487e0cd Further further rarity tweaking for berries 2023-09-23 21:07:38 +10:00
Hiroku bcf3fe1dc6 Merge branch 'battle-advancer' into 'main'
Battle Victory Advancement fix and tweaks

Closes #470

See merge request cable-mc/cobblemon!635
2023-09-23 08:01:16 +00:00
MoeBoy76andHiroku 8d5d567153 Battle Victory Advancement fix and tweaks 2023-09-23 08:01:15 +00:00
Paul 0643bc8d58 Merge remote-tracking branch 'origin/main' 2023-09-23 13:29:06 +10:00
Paul 1c6633bfdc Further rarity tweaking for berries 2023-09-23 13:28:52 +10:00
Hiroku f876c5f6c8 Merge branch 'persistence-and-extra-flags' into 'main'
Implement persistence on Pokemon and flags for hide label and unbattleable.

See merge request cable-mc/cobblemon!634
2023-09-23 03:19:40 +00:00
Deltric 99744af491 Implement persistence on Pokemon and flags for hide label and unbattleable. 2023-09-21 21:06:51 -05:00
Paul 0cd79c6c8f Updated fabric-api to 0.89.0 2023-09-22 10:07:27 +10:00
Hiroku 37f508793d Merge branch 'sound' into 'main'
Probably final iteration of Pokeball sounds

See merge request cable-mc/cobblemon!632
2023-09-21 20:01:53 +00:00
SterrezoandHiroku 5807e4526e Probably final iteration of Pokeball sounds 2023-09-21 20:01:53 +00:00
Licious 63a42edb7f Merge branch 'more-crop-gen-fixes' into 'main'
Many crop fixes

Closes #476, #494, and #495

See merge request cable-mc/cobblemon!629
2023-09-21 19:43:23 +00:00
Paul 7baf8e079d Improve medicinal leek anti-beach from Miasmus 2023-09-21 18:39:11 +10:00
Hiroku 88105e070b Merge branch 'this-is-just-your-nature' into 'main'
Fixed mints not visually changing stats

See merge request cable-mc/cobblemon!630
2023-09-21 07:54:29 +00:00
Paul 98556c1b24 Made medicinal leeks substantially rarer 2023-09-20 20:29:26 +10:00
Paul 137f080ba9 Fixed mints not visually changing stats 2023-09-20 20:27:50 +10:00
Paul 4f3601ab3b Added beach exclusion to medicinal leeks 2023-09-20 20:15:26 +10:00
Paul e523c46594 Improve rarity of generating big roots and only generate them with good space (and not water) 2023-09-20 20:10:14 +10:00
Paul 251ebe2a37 Improve berry generation in jungles by being able to replace leaves with them 2023-09-20 20:09:45 +10:00
Paul 12191b5d96 Fixed big roots not generating from stone 2023-09-20 20:09:24 +10:00
Paul 4da74f0bfb Fixed issues with wild berries not generating berries if spawned at mature age, reworked growth point timing 2023-09-20 20:09:11 +10:00
Paul b893681af5 Made berry groves rarer again 2023-09-20 20:08:34 +10:00
Paul 64a2b9c60f Updated berry data because a bunch of positions were missing 2023-09-19 20:25:11 +10:00
Nick 23e2d56db4 Merge branch 'root-and-herb-fixes' into 'main'
Root and herb fixes

See merge request cable-mc/cobblemon!628
2023-09-17 06:58:52 +00:00
Hiroku 3451a70cfd Micro-optimized at Nick's request 2023-09-17 05:39:27 +00:00
Paul 967053824b Made revival herbs rarer 2023-09-17 09:04:33 +10:00
Paul 3c3ef6a387 Fixed roots being able to spread forever 2023-09-17 09:04:14 +10:00
Paul 309de12a0e Fixed big roots being too rare 2023-09-17 09:03:52 +10:00
Hiroku 9b1699b507 Merge branch 'discovered-advanced-typoment' into 'main'
Update catch_all_vivillon.json

See merge request cable-mc/cobblemon!627
2023-09-16 00:29:53 +00:00
dhi_holoandHiroku f0cf94b830 Update catch_all_vivillon.json 2023-09-16 00:29:52 +00:00
Nick ddab45148c Merge branch 'mod-compat' into 'main'
Mod compat

See merge request cable-mc/cobblemon!626
2023-09-15 23:30:13 +00:00
HirokuandNick 2a23c77da7 Mod compat 2023-09-15 23:30:12 +00:00
Hiroku 9a4fcb15b2 Merge branch 'mint-generation' into 'main'
Fixed mint generation

See merge request cable-mc/cobblemon!625
2023-09-15 11:49:27 +00:00
Paul 07bb57c009 Buffed mints spawning 2023-09-15 21:30:08 +10:00
Paul 2934e38077 Fixed mints not generating 2023-09-15 20:32:31 +10:00
Hiroku d60592b5e4 Merge branch 'berry-gen-fixes' into 'main'
Fixed berries generating midair or where water was, fixed snowy grass blocks...

Closes #478

See merge request cable-mc/cobblemon!624
2023-09-15 08:36:12 +00:00
Paul 66e1f621cf Fixed berries generating midair or where water was, fixed snowy grass blocks with berries on them, made berries rarer. Closes #478 2023-09-15 13:52:46 +10:00
Hiroku 63d84df283 Merge branch 'implement_pokemon' into 'main'
Last of Pokémon Implemented.

See merge request cable-mc/cobblemon!623
2023-09-15 03:49:07 +00:00
TyzillionandHiroku 94400e61f5 Last of Pokémon Implemented. 2023-09-15 03:49:07 +00:00
Hiroku 029e120b2c Merge branch '1-point-4-bug-fixes' into 'main'
Some bug fixes

Closes #456 and #481

See merge request cable-mc/cobblemon!622
2023-09-15 00:05:53 +00:00
MiaandHiroku 7a305792af Some bug fixes 2023-09-15 00:05:53 +00:00
Nick 06ed32e4cb Merge branch 'issue-484' into 'main'
Fixes issue #484 (this is what you get for using regexes Hiroku)

Closes #484

See merge request cable-mc/cobblemon!621
2023-09-13 20:16:10 +00:00
apion ee63bf0eab Fixes issue #484 (this is what you get for using regexes Hiroku) 2023-09-13 15:54:18 -04:00
Hiroku 48a4add7aa Merge branch 'berry' into 'main'
Berry hitboxes, berry reordering

Closes #444 and #428

See merge request cable-mc/cobblemon!617
2023-09-12 11:34:28 +00:00
Paul dc8d7e36e0 Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/block/BerryBlock.kt
2023-09-12 20:52:23 +10:00
Paul 1c1a683f2d Fixed exploding pasture blocks crashing servers on Forge, closes #472 2023-09-12 20:36:35 +10:00
Paul 0ee4f60391 Fixed bad registration of processor types on Forge 2023-09-12 20:26:52 +10:00
Paul 4a7893f647 Fixed species.create not working for some species with weird names, added /clearpc command 2023-09-12 19:51:12 +10:00
Paul 76890135ef Fixed many items not being possible to give to pokemon by shift rc 2023-09-12 18:48:12 +10:00
Hiroku 930f95d3b6 Merge branch 'sound' into 'main'
Sound updates and consistency fixes

See merge request cable-mc/cobblemon!619
2023-09-12 07:49:54 +00:00
SterrezoandHiroku 0c15d14f1a Sound updates and consistency fixes 2023-09-12 07:49:54 +00:00
Hiroku a2bb0fb3cd Merge branch '1-point-4-bug-fixes' into 'main'
1.4 Bug Fixes

Closes #471, #455, #385, #445, #456, and #463

See merge request cable-mc/cobblemon!620
2023-09-12 07:49:14 +00:00
miasmus a97753c339 add block break particles to mulch removal 2023-09-11 22:48:47 -04:00
miasmus 557dda4c1d holy crap im so smrat (fixes harvesting berries with bonemeal in hand not playing interact anim) 2023-09-11 22:03:25 -04:00
miasmus 944fdf7c0c no one told me to test before pushing to main 2023-09-11 17:06:33 -04:00
miasmus ada8bc9403 Merge main into 1-point-4-bug-fixes 2023-09-11 16:56:03 -04:00
Paul 2d3d245b03 Fix pikachu evolution properly apparently and fix Nincada -> Shedinja direct evo being a thing 2023-09-11 19:57:03 +10:00
Paul 6c91342954 Licenses mf, do you speak it? 2023-09-11 17:45:52 +10:00
Hiroku 98b84d853c Merge branch 'screw-mark-removed' into 'main'
markRemoved bad, PreEmptsExplosion good

See merge request cable-mc/cobblemon!618
2023-09-11 06:55:45 +00:00
NeovitalismandHiroku 83ee5f3c3d markRemoved bad, PreEmptsExplosion good 2023-09-11 06:55:45 +00:00
Paul 137088296e Merge remote-tracking branch 'origin/main' into berry 2023-09-11 16:50:42 +10:00
Paul e748059052 Basic protections against blocks being switched underneath a pasture BE's nose 2023-09-11 15:15:35 +10:00
Paul f515a2ef72 Merge remote-tracking branch 'origin/main' into berry 2023-09-11 14:45:37 +10:00
Paul 0c9017d5e2 Merge remote-tracking branch 'origin/berry' into berry 2023-09-11 14:44:57 +10:00
Paul fd12789791 Colliders for all the berry trees 2023-09-11 14:44:38 +10:00
Hiroku 638c92ec4c Merge branch 'starter-ball' into 'main'
Chested Balls

Closes #461

See merge request cable-mc/cobblemon!616
2023-09-11 04:26:17 +00:00
dhi_holoandHiroku bee865931a Chested Balls 2023-09-11 04:26:16 +00:00
miasmus ad039cb949 re-order berries, fixes #428 2023-09-10 12:16:26 -04:00
miasmus 0436b36872 partially heal PP when sleeping (fixes #463) 2023-09-10 11:58:42 -04:00
Hiroku 8a62088fb4 Merge branch 'battle_gimmicks' into 'main'
Mega Evolution, Z-Moves, Dynamax, and Terastallization

See merge request cable-mc/cobblemon!572
2023-09-10 09:54:30 +00:00
Segfault GuyandHiroku 7e2bb0ca6a Mega Evolution, Z-Moves, Dynamax, and Terastallization 2023-09-10 09:54:29 +00:00
Paul 228347e46f Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/StructurePoolGeneratorMixin.java
#	common/src/main/kotlin/com/cobblemon/mod/common/world/CobblemonStructures.kt
2023-09-10 16:20:04 +10:00
Paul 3a2c12c652 Fixed displayName in spawn files not working properly. Closes #320 2023-09-10 15:11:45 +10:00
Paul a6945adf5a Added optional 'enabled' property to model layers so that they can be disabled later in resolver variations. Closes #373 2023-09-10 15:08:17 +10:00
Paul 295e68b0d5 Poison Heal Pokémon will heal if poisoned out of battle. Closes #373 2023-09-10 14:43:29 +10:00
Paul be983f6115 Fixed an apparently unsafe cast to client world for Litematica, closes #375 2023-09-10 14:27:56 +10:00
Paul 6eeda04b78 Fixed reused models from multiple posers causing rotation problems, closes #414 2023-09-10 14:26:58 +10:00
Paul c03097e627 Fixed quirks being idle-preventing from resource packs, closes #429 and closes #419 2023-09-10 14:14:35 +10:00
Paul a330f95559 Improve sneaking positioning of shoulder mounts 2023-09-10 11:12:53 +10:00
Paul 25d7c8376e Fixed shoulder mount animations being a blur 2023-09-10 11:05:50 +10:00
Paul bc93bae9ab Check the form during readfromnbt/json 2023-09-10 11:01:32 +10:00
Paul 4f9ca14301 Fixed evolutions being blank for forms that rely on root-species evos 2023-09-10 11:01:12 +10:00
Paul b9515a2a81 Fixed more nicknaming glitches 2023-09-10 11:00:54 +10:00
miasmus 6200e3a693 update changelog 2023-09-09 19:35:12 -04:00
Paul 7675148d37 Fixed inconsistency with sending out tracing 2023-09-10 08:45:42 +10:00
miasmus 03b29ce468 fix leeks, for real this time (fixes #456) 2023-09-09 18:18:53 -04:00
Paul d2668a15b0 Grouped structure maximums and limited berry farms to 2 total per village 2023-09-10 08:05:21 +10:00
miasmus d9a99ad65b Fix players being able to battle/trade in spectator mode 2023-09-09 17:42:41 -04:00
miasmus 0fc9d29884 fix leeks generating on ice 2023-09-09 17:16:39 -04:00
Hiroku f500ab4a87 Merge branch 'berry' into 'main'
Reworked berry generation in villages using their own structures, rarity & gen fixes

Closes #444

See merge request cable-mc/cobblemon!615
2023-09-09 20:55:30 +00:00
Hiroku 19a85276af Reworked berry generation in villages using their own structures, rarity & gen fixes 2023-09-09 20:55:30 +00:00
Paul 27a4b3b6f4 Reworked berry generation in villages using their own structures 2023-09-09 20:28:42 +10:00
Paul 5df79f083a Fixed wild berry trees being still too common and surviving on air 2023-09-09 20:26:28 +10:00
Paul 1501c3689a Merge remote-tracking branch 'origin/main' into berry 2023-09-09 19:20:44 +10:00
Hiroku 277ec8ffcd Merge branch 'lootchestplate' into 'main'
chestlootplate

See merge request cable-mc/cobblemon!614
2023-09-09 02:52:19 +00:00
dhi_holoandHiroku c7b82adfb0 chestlootplate 2023-09-09 02:52:19 +00:00
Paul 98e495f7e4 Merge remote-tracking branch 'origin/main' into berry 2023-09-09 08:53:50 +10:00
Paul 508e607440 Merge remote-tracking branch 'origin/main' 2023-09-09 08:48:38 +10:00
Paul ab69588331 Flying AI improvements 2023-09-09 08:48:26 +10:00
Hiroku b912e8cbca Merge branch 'berry' into 'main'
Berry fixes including mulch visibility

Closes #444

See merge request cable-mc/cobblemon!613
2023-09-08 19:55:32 +00:00
Hiroku cc2eaf6c1a Berry fixes including mulch visibility 2023-09-08 19:55:32 +00:00
Paul a608cbe0fe Merge conflicty stuff 2023-09-08 19:46:18 +10:00
Paul a4ed9393f0 Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/block/BerryBlock.kt
2023-09-08 19:46:05 +10:00
Paul f294d4fcef Fixed berry juice, energy root, heal powder, potions, and status cure items not being consumed when used outside of battle 2023-09-08 19:43:12 +10:00
Paul 6046ca10d0 Merge remote-tracking branch 'origin/berry' into berry 2023-09-08 19:39:09 +10:00
Paul d24371b9e4 Added mulch visibility for berry trees, and fixed surprise mulch on revival herbs 2023-09-08 19:37:18 +10:00
Hiroku 645d84858e Merge branch 'unloaded-pasture-fix' into 'main'
Fix removing pokemon from an unloaded pasture

See merge request cable-mc/cobblemon!612
2023-09-08 08:09:13 +00:00
NeovitalismandHiroku 04ecd3d976 Fix removing pokemon from an unloaded pasture 2023-09-08 08:09:13 +00:00
Paul c02fe8a00c Fixed hand not swinging when applying mulch 2023-09-08 16:33:28 +10:00
Hiroku 008e42a267 Merge branch 'berry' into 'main'
Latest Berry Updates

Closes #444

See merge request cable-mc/cobblemon!606
2023-09-08 06:29:28 +00:00
NickandHiroku 316ab772bf Latest Berry Updates 2023-09-08 06:29:28 +00:00
Hiroku bca4b06750 Should return pass not fail for isSneaking interaction check 2023-09-08 06:29:04 +00:00
Hiroku dea1d74e40 Merge branch 'fix-battle-particle-misrendering' into 'main'
Fix Issues Related to RenderContext Changes

See merge request cable-mc/cobblemon!611
2023-09-08 05:30:17 +00:00
NickandHiroku 12f1259495 Fix Issues Related to RenderContext Changes 2023-09-08 05:30:17 +00:00
Lily cc30f4f207 fix sneaking checks when using items on pokemon 2023-09-08 00:51:27 -04:00
Hiroku 775a2038d0 Merge branch 'shiny_starters_gamerule' into 'main'
add shiny starters gamerule

See merge request cable-mc/cobblemon!557
2023-09-08 04:51:07 +00:00
LilyandHiroku 1dc8ae036f add shiny starters gamerule 2023-09-08 04:51:07 +00:00
Paul 0011993afb Fixed wiki berry pivot 2023-09-08 13:59:49 +10:00
Hiroku 1ef22ad52a Merge branch 'pokemon-item-tint' into 'main'
Added in tint nbt for Pokemon items

See merge request cable-mc/cobblemon!608
2023-09-08 03:16:33 +00:00
Hiroku e8030c0180 Merge branch 'fix_446' into 'main'
Fix Drag Dispatch.

Closes #446

See merge request cable-mc/cobblemon!610
2023-09-08 03:16:24 +00:00
Segfault Guy e929f6f02d Fix Drag Dispatch. 2023-09-07 22:56:22 -04:00
Deltric 9518422f62 Added in tint nbt for Pokemon items 2023-09-07 20:17:12 -05:00
Lily 4c03891f19 berry grove trees now only spawn at age 3 or higher ( Closes #445 ) 2023-09-07 21:16:13 -04:00
Hiroku 52a3bf50d0 Merge branch 'armor-sprites' into 'main'
updated sprites for auspicious and malicious armor + clarified what a renderable pokemon is

See merge request cable-mc/cobblemon!607
2023-09-08 00:52:28 +00:00
LilyandHiroku c1657b5772 updated sprites for auspicious and malicious armor + clarified what a renderable pokemon is 2023-09-08 00:52:28 +00:00
Lily e57c9487ca prevent berry groves from replacing water blocks 2023-09-07 20:13:36 -04:00
miasmus 7e583d6319 fix itemgroup, add descriptions for 96 pokemon, fixes #455 2023-09-07 19:49:00 -04:00
Paul 11256c0e3a Fixed berry flowers not rendering 2023-09-08 09:41:34 +10:00
Paul ffb53626eb Only allow mulching berry trees that are pre-flowering and are on farmland 2023-09-08 09:27:34 +10:00
Paul a74671f710 Re-delete unused mixin [skip-ci] 2023-09-08 09:09:25 +10:00
Hiroku 04d83251b4 Merge branch 'evolution-changes-&-fixes' into 'main'
Evolution changes & fixes

Closes #287, #386, and #398

See merge request cable-mc/cobblemon!582
2023-09-07 23:04:40 +00:00
LiciousandHiroku bece6dfbb0 Evolution changes & fixes 2023-09-07 23:04:40 +00:00
miasmus 5e5cb4e388 no more pokemon waterboarding (fixes #385) 2023-09-07 18:02:01 -04:00
Hiroku 8f9dc451f1 Merge branch 'a-guide-to-1-point-4' into 'main'
Advancements (from hell)

See merge request cable-mc/cobblemon!543
2023-09-07 21:48:19 +00:00
dhi_holoandHiroku ffb1c94c3f Advancements (from hell) 2023-09-07 21:48:19 +00:00
Lily d9b2f0cef8 Merge remote-tracking branch 'origin/berry' into berry 2023-09-07 17:18:20 -04:00
Lily 458fa1732c make berry groves 3x rarer 2023-09-07 17:17:38 -04:00
miasmus 23e532a214 fixed compile errors from merge 2023-09-06 16:53:57 -04:00
Nick 70f1e5e621 Fix conflicts between main and berry branch 2023-09-06 17:12:04 +00:00
Nick 1e8ae04ad5 Merge branch 'gui-model-hook' into 'main'
Add reference to previously polled species name and aspects.

See merge request cable-mc/cobblemon!594
2023-09-06 03:00:38 +00:00
WaterpickerandNick 0cfa7bf61d Add reference to previously polled species name and aspects. 2023-09-06 03:00:37 +00:00
miasmus 99a88e66e7 Fixes #444 (some berries not consumed on use outside of battle) 2023-09-05 16:07:59 -04:00
ApionXD 0240f925f6 Fixed bug where tall grass wouldn't be deleted when a berry tree replaced its lower block 2023-09-05 10:32:12 -04:00
Hiroku aa2532df6f Merge branch 'x-treme-crafting' into 'main'
X-Item Recipes

See merge request cable-mc/cobblemon!604
2023-09-05 11:52:28 +00:00
dhi_holoandHiroku 0c14e5dedc X-Item Recipes 2023-09-05 11:52:28 +00:00
Hiroku 62248ff97c Merge branch 'long_lost_logs' into 'main'
Oops! Forgot to update the changelogs. No one cares about these, right?

See merge request cable-mc/cobblemon!603
2023-09-05 10:54:13 +00:00
Segfault GuyandHiroku 17343abce2 Oops! Forgot to update the changelogs. No one cares about these, right? 2023-09-05 10:54:13 +00:00
Hiroku 0ff73d0c21 Merge branch 'auspicious_armor' into 'main'
adds auspicious and malicious armor, equipping functionality is in the earlier...

Closes #436

See merge request cable-mc/cobblemon!602
2023-09-05 09:56:57 +00:00
LilyandHiroku 77da1f8252 adds auspicious and malicious armor, equipping functionality is in the earlier... 2023-09-05 09:56:57 +00:00
Hiroku 18301300aa Merge branch 'implement_pokemon' into 'main'
So so so many Pokemon implemented and models, animations, and textures updated

See merge request cable-mc/cobblemon!600
2023-09-05 08:59:10 +00:00
TyzillionandHiroku ca044e8390 So so so many Pokemon implemented and models, animations, and textures updated 2023-09-05 08:59:10 +00:00
miasmus a6b68b4076 Adds other crops to maintains_farmland tag 2023-09-04 19:50:26 -04:00
miasmus dbce3252ab Adds a #cobblemon:berry tag with all the berry blocks, adds them to the #maintains_farmland tag 2023-09-04 19:29:33 -04:00
Lily cc3b09f590 added missing particles for figy, micle, passho, starf, and wacan berries 2023-09-04 19:24:23 -04:00
Lily 8ed2b820f2 prevent player from interacting with pokemon when sneaking 2023-09-04 17:25:56 -04:00
Paul ff2db8fa11 You saw nothing 2023-09-04 18:49:38 +10:00
Paul 42d330e1c0 Added berry world gen weights to data 2023-09-04 18:45:27 +10:00
Paul 062cee2b9a Merge remote-tracking branch 'origin/main' 2023-09-04 18:16:36 +10:00
Paul 5730d812a0 Randomize berry positions for most berry trees (except for berry swords) 2023-09-04 18:16:28 +10:00
Hiroku cb4ad09a11 Merge branch 'heal-me-up-scotty' into 'main'
Potions are like healing berries

Closes #437

See merge request cable-mc/cobblemon!598
2023-09-04 05:52:07 +00:00
MoeBoy76andHiroku 1d81df91eb Potions are like healing berries 2023-09-04 05:52:06 +00:00
Hiroku d962d30074 Merge branch 'fix-starter-volume' into 'main'
Fixes starter selection sound effect playing twice when clicking the arrow keys to navigate

Closes #427

See merge request cable-mc/cobblemon!599
2023-09-04 05:47:31 +00:00
Hiroku 5096d9a15a Merge branch 'pokemonloot-fix' into 'main'
Fixes pokemon dropping XP when doPokemonLoot gamerule is set to false

See merge request cable-mc/cobblemon!597
2023-09-04 04:20:21 +00:00
Hiroku 24737cf606 Merge branch 'selected-pokemon-pvp-fix' into 'main'
Fix player battle always starting with first pokemon

Closes #389

See merge request cable-mc/cobblemon!579
2023-09-04 04:12:14 +00:00
MiaandHiroku 7bd1c24b57 Fix player battle always starting with first pokemon 2023-09-04 04:12:13 +00:00
miasmus 36b1bec04b Fixes starter selection sound effect playing twice when clicking the arrow keys to navigate 2023-09-03 20:19:52 -04:00
Hiroku 7f14b60efb Merge branch 'neo-1.4-fixes' into 'main'
Various bug fixes

See merge request cable-mc/cobblemon!596
2023-09-03 22:27:52 +00:00
NeovitalismandHiroku 239559cfd3 Various bug fixes 2023-09-03 22:27:52 +00:00
miasmus c4b2a1546a Fixes pokemon dropping XP when doPokemonLoot gamerule is set to false 2023-09-03 13:39:51 -04:00
Paul 278b36234e Put the pre-render call back so pokeballs animate (oopsy) 2023-09-03 14:43:49 +10:00
Paul 1598c8209a Fixed ternary and boolean-not operators not working in Molang which broke berry friendship increase amounts 2023-09-03 14:38:44 +10:00
apion 1d353bdc5b Added hitboxes to the early stages of berry tree growth 2023-09-02 03:37:31 -04:00
apion df7109ba45 Added flower model/texture, fixed particles in all the tree models 2023-09-02 03:15:39 -04:00
Hiroku deb7ece946 Merge branch 'berry' into 'main'
Berries

See merge request cable-mc/cobblemon!595
2023-09-02 06:05:40 +00:00
ApionandHiroku d8f9f8926b Berries 2023-09-02 06:05:40 +00:00
apion 457e7b65c9 Merge remote-tracking branch 'origin/berry' into berry 2023-09-02 01:36:35 -04:00
apion 755d4ea612 Added early stage growth models to all block states 2023-09-02 01:36:27 -04:00
Hiroku ad6bcb1572 Licenses fix (apion banned) 2023-09-02 05:28:15 +00:00
apion 42235a35ee Final asset sync (hopefully) 2023-09-02 00:59:01 -04:00
apion d6e51b9c55 Merge remote-tracking branch 'origin/berry' into berry 2023-09-01 21:27:00 -04:00
apion d2970c10b3 Added texture atlases for berries, fixed issue in Forge where berry models werent being loaded 2023-09-01 21:26:45 -04:00
Hiroku ca54a0abf0 Last one was stereo (THANKS STERR) 2023-09-01 09:18:13 +00:00
Hiroku 1ad743a126 Added berry eat sound effect 2023-09-01 09:16:52 +00:00
Paul cde5a5b408 Added berry juice, fixed Touga sprite/blockstates missing 2023-09-01 19:03:43 +10:00
Paul e6a45fd356 Removed dead code in berry interactions, added medicinal brew sprite, changelog updates 2023-09-01 16:57:00 +10:00
apion c3eb05e5c6 More asset sync (again) (YAY!) Also disabled berry tinting 2023-08-31 19:54:26 -04:00
apion a82dee2beb More asset sync (again) (YAY!) Also disabled berry tinting 2023-08-31 19:54:05 -04:00
apion 8099bfe330 Merge branch 'main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/util/MoLangExtensions.kt
2023-08-31 19:11:18 -04:00
Paul 9d8803caf2 Make entity accessible from poseable entity state 2023-09-01 09:01:12 +10:00
apion 95d6a1c183 More asset sync 2023-08-31 00:08:02 -04:00
apion 4fd433b072 Actually fixed berries rendering when off screen (you were right the first time Hiroku) 2023-08-28 21:10:29 -04:00
apion dc6804bba6 Moved mulch logic into the berry block entity 2023-08-28 18:50:55 -04:00
Paul 08827a742f Fixes to particle effects in bedrock animations 2023-08-28 18:27:19 +10:00
Paul 16a0cd1fe7 Updated heaps of berry assets and placements 2023-08-27 21:50:38 +10:00
Paul a6a76052a6 Fixed various items not preventing off-hand interactions from playing 2023-08-27 15:58:57 +10:00
Paul abeb005bbb Improve error logging for bad MoLang expression parsing from String.asExpression 2023-08-27 13:05:03 +10:00
Paul 6835232aa6 Added support for effect looping in non-loop animations 2023-08-27 13:04:47 +10:00
Paul 78ebc9a9eb Added MoLang instructions: s.sound(effect, volume, pitch) and s.random(...) 2023-08-27 13:04:06 +10:00
Paul f5e85971f3 Made Pokémon appear red when hurt 2023-08-27 11:11:53 +10:00
Hiroku e85b2aa138 Merge branch 'battle_bugs_1' into 'main'
Fix Shedinja HP and Faint Softlock.

Closes #341 and #400

See merge request cable-mc/cobblemon!593
2023-08-26 02:33:53 +00:00
Segfault Guy 371e1426a3 Fix Shedinja HP and Faint Softlock. 2023-08-25 22:24:54 -04:00
Hiroku 256e09846d Merge branch 'main' into 'main'
Made Master Balls Fireproof (won't disappear when thrown in lava/fire)

See merge request cable-mc/cobblemon!591
2023-08-26 01:49:13 +00:00
RetroandHiroku dd2d441b0c Made Master Balls Fireproof (won't disappear when thrown in lava/fire) 2023-08-26 01:49:13 +00:00
Nick ddc57ecd8d Merge branch 'fixed-tag-file' into 'main'
Fixed tag file

See merge request cable-mc/cobblemon!592
2023-08-24 19:50:57 +00:00
Deltric 202a6a0496 Fixed tag file 2023-08-24 14:26:32 -05:00
Hiroku fbc639cbf3 Merge branch 'power-items' into 'main'
Added in Power Items

See merge request cable-mc/cobblemon!580
2023-08-24 09:38:51 +00:00
DeltricandHiroku 78faf30b32 Added in Power Items 2023-08-24 09:38:51 +00:00
Hiroku 39cbb448a6 Merge branch 'poke-select' into 'main'
Update Poke Select Interface

See merge request cable-mc/cobblemon!588
2023-08-22 09:21:51 +00:00
TestandHiroku fd10f8efd8 Update Poke Select Interface 2023-08-22 09:21:51 +00:00
Hiroku 9161f61017 Merge branch 'spawn-issue' into 'main'
Bug fixes

Closes #402 and #403

See merge request cable-mc/cobblemon!577
2023-08-22 09:08:40 +00:00
ApionandHiroku 4244680956 Bug fixes 2023-08-22 09:08:40 +00:00
Hiroku 92ad81f8d1 Merge branch 'sound' into 'main'
Various sound updates and volume tweaks

See merge request cable-mc/cobblemon!587
2023-08-22 09:06:17 +00:00
SterrezoandHiroku c1c7106bff Various sound updates and volume tweaks 2023-08-22 09:06:17 +00:00
Hiroku e36b1b22c4 Merge branch 'main' into 'main'
Persistent Data Fix

See merge request cable-mc/cobblemon!589
2023-08-22 09:05:39 +00:00
ItsJustChris85 e21d445e14 Persistent Data Fix 2023-08-22 00:51:00 -04:00
apion 22d66103b6 Added missing berries, fix desert grove spawning, decrease chance of groves 2023-08-20 06:14:33 -04:00
apion 0e2a0af914 Asset sync 2023-08-20 05:07:49 -04:00
Paul 098ff1f96b Clamp set level within bounds instead of throwing exceptions (means we don't corrupt storages) 2023-08-20 17:21:25 +10:00
Paul f93445f081 JvmOverloads in battle builder, closes #410 2023-08-20 17:06:50 +10:00
Paul 85a962e924 Licenses 2023-08-20 15:13:23 +10:00
Paul f6d0f11c99 Implemented uncatchable, fixed some capture calculation issues, added PokeBallCaptureCalculatedEvent and PokemonCatchRateEvent 2023-08-20 13:51:08 +10:00
Paul e74b838fb2 Fixed lighting of pokemon model items 2023-08-20 10:35:29 +10:00
apion 7591345181 Fixed probability calculations 2023-08-18 06:53:35 -04:00
apion 8d157a5ad5 Added berry spawns to villages, add licenses 2023-08-18 05:50:06 -04:00
apion 8b857881a8 Merge branch 'implementing-naturally-generated-structures' into berry 2023-08-17 05:09:26 -04:00
apion 995a8c2dc0 Got structure processor + overrides working 2023-08-17 05:08:00 -04:00
apion 4dc7accef9 Testing mixin 2023-08-16 19:07:53 -04:00
apion 90b8923b98 Merge branch 'main' into implementing-naturally-generated-structures
# Conflicts:
#	common/src/main/resources/cobblemon-common.accesswidener
#	common/src/main/resources/mixins.cobblemon-common.json
#	fabric/src/main/kotlin/com/cobblemon/mod/fabric/CobblemonFabric.kt
#	forge/src/main/kotlin/com/cobblemon/mod/forge/CobblemonForge.kt
2023-08-15 22:17:11 -04:00
apion 9a7e4d05f0 Added JEI integration for berry mutations 2023-08-15 21:35:35 -04:00
Paul 8d981d1b05 Added brewing recipes for all the medicine 2023-08-16 08:45:13 +10:00
apion f5bf60dd37 Added some caching to berry generation 2023-08-14 15:39:53 -04:00
apion 585eb2a946 Add missing license headers 2023-08-14 14:35:52 -04:00
apion ee38a90aad Berry data update 2023-08-14 14:33:10 -04:00
apion d9fc5f5c90 Added system for controlling where berries spawn 2023-08-14 14:32:51 -04:00
apion 5d9be82e8f Added random spawn of berries. (Need system to disable certain berries from spawning) 2023-08-14 04:18:39 -04:00
apion 808a238d2a Swapped to deserializing preferred biomes to actual tags 2023-08-14 03:54:18 -04:00
apion 0a505bec75 Got growth mulch working. Also added a block ticker to berry trees for more controlled growth times 2023-08-13 02:19:05 -04:00
apion f561141315 Got all mulches except growth working for berries 2023-08-12 21:04:50 -04:00
apion 8419b140a7 Berry data update 2023-08-12 20:13:06 -04:00
apion 12824f6f4f Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
2023-08-12 14:37:47 -04:00
Paul 60df079794 Improve performance of berry tree rendering by only rendering berries when they are on-screen 2023-08-12 18:43:50 +10:00
apion 1924862fcb Fixed renderer again (please let this be the last time) and fixed pecha clipping 2023-08-12 00:45:00 -04:00
apion dba3de83fa Updated script to grab tint index values from asset repo, fixed renderer (Hiroku) and updated colors (again) 2023-08-11 19:48:09 -04:00
apion df30a209a5 Once again, synced the mature tree model, berry textures, and berry geographies for berries 1-24 from the asset repo 2023-08-10 21:30:01 -04:00
apion 0fea0966c0 Added berry geographies 2023-08-10 19:53:25 -04:00
apion 727fc969fa Hopefully fixed rendered? 2023-08-10 19:30:17 -04:00
apion c17ae13578 Fixed typo 2023-08-10 16:17:45 -04:00
apion 8e09051bf7 Synced some textures from the asset repo, BerryBlockRenderer changes. 2023-08-10 15:58:27 -04:00
apion 4f03643840 Synced models for berries 12-24 to asset repo 2023-08-09 19:43:42 -04:00
apion 6cdab8f296 Updated models for berries 1-12 to the ones from the asset repo, add tints for berries 1-12 2023-08-09 16:28:57 -04:00
apion fc1bade1d1 Fixed a few model typos, made berry foliage colors fall back to leaf colors 2023-08-09 14:31:39 -04:00
apion 544efc80d5 Update all berry data to come from the GDrive spreadsheets 2023-08-08 12:19:30 -04:00
apion 43fe97a6fe Added a growth factor to berries affected by their current biome. Also added script to generate berry data from spreadsheet data 2023-08-08 12:02:25 -04:00
nlingebretson 965196da2e Added placement data for berry trees 1-14 2023-08-06 13:36:35 -05:00
Paul 72c4cc0992 Undid battle debug stuff because I forgot, again 2023-08-05 17:10:47 +10:00
Paul b0838e88fd Put berries in the agriculture tab instead 2023-08-05 17:09:52 +10:00
Paul 925d7ce2dc Added berry interaction + bag item effects, berry eat sound hook, berry eating lang, berries to creative tab 2023-08-05 17:08:00 +10:00
Paul a4000a6c0f Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-08-04 11:19:50 +10:00
Lily f6cbadff61 mulch can now be used on berry trees and removed using a shovel 2023-07-17 20:05:10 -04:00
Lily e10e048e2c Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2023-07-16 20:18:05 -04:00
Paul 506aafbdf0 Merged from main and added berries to creative menu 2023-07-01 21:25:20 +10:00
Paul 814bba5273 Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/VaryingRenderableResolver.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2023-07-01 21:08:11 +10:00
Paul b5475b0e3e Multi-axis rotations on berries, cleaned rendering logic for pokeballs and berries, cleaned berry block entity logic 2023-07-01 21:05:32 +10:00
nlingebretson c60a3939a4 We sent the berries to flavor town (not clickbait) - added flavor values to berries and pointed each berry tree to use correct models for flowering berries 2023-06-27 20:53:54 -05:00
nlingebretson c0c49cc951 Updated growth time and refresh rate in berry data files, replacing the previous lifecycle data 2023-06-25 12:39:26 -05:00
nlingebretson fe08a94540 Updated Base Yield and Bonus Yield (bonus is temporary stored in growthFactors - will move to preferred biomes) 2023-06-25 11:19:32 -05:00
Paul 9a03c17a0c Berry stuff on 1.19.4 and fixed some missing data 2023-06-11 20:56:54 +10:00
Paul 1bc2295b70 Added pokeball entity to capture event 2023-06-11 19:16:56 +10:00
Paul 37cd4ff11b Merge remote-tracking branch 'origin/main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/interaction/PokemonEntityInteraction.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/item/Berries.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/item/Berry.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/net/ClientPacketRegistrar.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/CobblemonItemGroups.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/CandyItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/InteractiveItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/LinkCableItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/interactive/VitaminItem.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/util/NetExtensions.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/textures/block/berry_leaf.png
#	common/src/main/resources/assets/cobblemon/textures/block/coba_tree.png
#	common/src/main/resources/assets/cobblemon/textures/block/leaf.png
#	common/src/main/resources/assets/cobblemon/textures/block/rawst_tree.png
#	common/src/main/resources/assets/cobblemon/textures/block/vines.png
2023-06-11 14:30:03 +10:00
nlingebretson 5f9108400b Rework of berries to include a growth time and refresh rate 2023-06-10 21:04:57 -05:00
nlingebretson 0245d82250 Updated item sprite to use the up-to-date sprites in the repo and added base json files for berry data (incomplete) 2023-05-17 20:28:06 -05:00
nlingebretson 4bd30be29f Added lang data for berries and tooltips (part 1) 2023-04-15 19:20:58 -05:00
nlingebretson 0b52918d8d Pointed textures on trees to updated files. 2023-04-15 14:42:54 -05:00
bcheevers123 9b73565e28 Added Texture, Young Declare, Mature Declare, Blockstate, Item Icon, Item Declare. Some berrry trees missing textures. Lang and Berry Data need added. 2023-04-15 19:36:41 +01:00
bcheevers123 a1e04e8df2 Added Texture, Young Declare, Mature Declare, Blockstate, Item Icon, Item Declare. Some berrry trees missing textures. Langf and Berry Data need added. 2023-04-15 19:35:48 +01:00
nlingebretson 51a542b6a9 Added Aguav and Cheri Berry implementation examples 2023-04-15 10:15:28 -05:00
nlingebretson f0e8d80da4 Added texture/item files and added items in the declaration portion 2023-04-10 20:21:58 -05:00
Yaseen 532bf008d3 shorter desert long path 2023-03-28 18:09:11 +01:00
Yaseen 701c727475 Shorter long_paths for snowy and desert 2023-03-28 18:08:55 +01:00
Yaseen 3df605c50f Fix desert and taiga pokecenter nbts 2023-03-28 17:33:27 +01:00
jmmcp ad97188902 Updated snowy pokecenter nbt 2023-03-28 00:59:52 -04:00
Yaseen 237edcc2fa a bunch of code clean up 2023-03-27 17:18:10 +01:00
Yaseen 51ab2c4fd8 Tidy up access wideners 2023-03-27 16:31:28 +01:00
Yaseen c0a482a92a better desert village nbt 2023-03-27 16:29:55 +01:00
Yaseen 829a063fe7 Add support for legacy pool elements (for paths) + use CobblemonStructureIDs 2023-03-27 15:47:37 +01:00
Yaseen f3f2fa11f7 Delete some temp files 2023-03-27 15:45:31 +01:00
Yaseen 298ba53ec2 taiga with stairs 2023-03-27 08:09:14 +01:00
jmmcp 28401e3c95 Updated taiga nbt and changed weights 2023-03-27 00:43:40 -04:00
Yaseen 3a0ed1a244 half fix for taiga pokecenter jigssaw (still need to make it turn to stairs) 2023-03-27 02:44:25 +01:00
Yaseen b27895a3c2 some refactoring 2023-03-27 02:27:05 +01:00
Yaseen e8647313b7 Streets + processors 2023-03-27 02:23:36 +01:00
Yaseen 7fdff93a25 add mossify processor for taiga pokecenter 2023-03-27 02:18:55 +01:00
Yaseen 06335ca854 better way of adding pokecenters to structure pool 2023-03-27 02:02:50 +01:00
Yaseen ba22800783 Some experiments 2023-03-19 23:45:14 +00:00
Yaseen 832f260472 Remove unused mixins 2023-03-19 22:51:17 +00:00
Yaseen df594f18d4 Some cleanup + small fix 2023-03-17 00:57:37 +00:00
Yaseen 5663d50a1c Better fix 2023-03-16 21:04:57 +00:00
Yaseen 373be37287 delete unused mixin 2023-03-16 15:06:51 +00:00
Yaseen 7071d74196 little bit of cleanup 2023-03-16 03:57:52 +00:00
Yaseen f5f3ad0d58 seems like its working 2023-03-16 03:39:05 +00:00
Yaseen 7e1b9b65f1 fix taiga pokecenter nbt 2023-03-14 21:14:10 +00:00
Yaseen 57839da643 Bunch of clean up 2023-03-14 01:48:30 +00:00
Yaseen 4a4dd66a65 A bunch of mixin stuff 2023-03-13 23:59:12 +00:00
jmmcp cfcf97bc88 Updated jsons and village plains pokecenter NBT jigsaw location. 2023-03-06 19:11:16 -05:00
jmmcp 1adf625044 Updated pokecenter NBT files to have proper structure void blocks and healers/pcs implemented.
Implemented natural generation of plains, desert, snowy, taiga, and savanna village pokecenters.
2023-03-05 21:55:47 -05:00
jmmcp a136ca106b Merge remote-tracking branch 'origin/implementing-naturally-generated-structures' into implementing-naturally-generated-structures
# Conflicts:
#	common/src/main/resources/data/cobblemon/structures/village_plains_pokecenter.nbt
#	common/src/main/resources/data/minecraft/worldgen/template_pool/village/plains/houses.json
2023-03-05 18:27:40 -05:00
Yaseen cbe1a79c2a increase the weights 2023-03-05 23:03:44 +00:00
Yaseen 093f953c7f more houses on long path 2023-03-05 18:22:46 +00:00
Yaseen dfd4fd252f Added long path and pokemon center plains 2023-03-05 18:15:39 +00:00
jmmcp e22befbc33 Working towards generation in villages 2023-03-04 23:40:19 -05:00
jmmcp 7bbb0eac56 Working towards generation in villages 2023-03-04 15:09:32 -05:00
jmmcp c42facec8a Added pokecenter nbt files and allowed village_plains_pokecenter to naturally generate. 2023-03-01 21:07:59 -05:00
Licious17 7d2663b082 Add a few more berry items 2023-02-03 20:41:51 +00:00
Licious17 43c6751039 Fix compile error 2023-02-03 15:24:39 +00:00
Licious17 f0fc94d25a Merge branch 'main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlockEntities.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/BerryBlock.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/BerryBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobbledDataProvider.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2023-02-03 15:19:26 +00:00
Licious17 10ba9c5f87 Added various API events related to the berry farming process. 2023-01-20 09:28:34 +00:00
Licious17 dcca1ec71f Add remaining interactions required for berry functionality. 2023-01-19 18:50:38 +00:00
Licious17 7847227a57 Remove unused import 2023-01-16 12:21:19 +00:00
Licious17 3f1660295e Implement berry mutation 2023-01-16 12:20:39 +00:00
Licious17 87f1063d6f Fix imports and licenses 2023-01-15 17:14:11 +00:00
Licious17 0951a7d73e Merge branch 'main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/tags/CobblemonBlockTags.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/net/ClientPacketRegistrar.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2023-01-15 17:06:02 +00:00
Licious17 256fcf3a8b Fixed berry flower and fruit not rendering for the first life cycle 2022-12-20 14:15:11 +00:00
Licious17 7e082f27e0 Remove reference to client only class in Berry API 2022-12-19 19:20:21 +00:00
Licious17 e81b8ca4f2 Removed VoxelShape for berry fruit and flowers, added customizable rotation to berry flower and fruits growth points. 2022-12-19 18:43:33 +00:00
Licious17 351910a3ef Merge branch 'main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobbledDataProvider.kt
2022-12-19 12:52:42 +00:00
Paul e700dcb142 Model rotation example in berry block renderer 2022-12-13 20:45:41 +10:00
Paul 2527b0e5d1 Corrected comment [skip-ci] 2022-12-13 20:34:32 +10:00
Paul 0fe36a98d7 Cleanup code [skip-ci] 2022-12-13 20:31:10 +10:00
Paul c9ef897a2e Fixed positioning of berry and flower rendering 2022-12-13 20:30:11 +10:00
Licious17 2cc46406de Updated pecha fruit texture, fixed berry rendering culling. 2022-12-10 13:43:04 +00:00
Licious17 1ef39bf1a3 Attempts at berry fruit and flower rendering 2022-12-09 10:09:19 +00:00
Licious17 e0bc16a232 Merge branch 'main' into berry 2022-12-08 16:17:01 +00:00
Paul f46109e080 Fixed berry trees not rendering 2022-12-08 19:30:58 +10:00
Licious17 21983061a6 Fix tint index of berry tree model being shared by the break particle 2022-12-06 16:05:45 +00:00
Licious17 87543095f1 Cleanup 2022-12-06 15:08:26 +00:00
Licious17 688b0836a5 Add missing doc 2022-12-06 15:04:53 +00:00
Licious17 42d2a5ce89 Added util extension for Box ByteBuf read/write 2022-12-06 15:03:20 +00:00
Licious17 307f5e2c8d Merge branch 'main' into berry 2022-12-06 12:12:51 +00:00
Licious17 b1a6b43414 Fix pecha berry Y voxelshape y positions 2022-12-05 14:57:30 +00:00
Licious17 8d90e2d190 Merge branch 'main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/data/CobbledDataProvider.kt
2022-12-05 14:51:37 +00:00
Licious17 767446c804 Implemented berry data synchronization with client 2022-12-05 14:49:50 +00:00
Licious17 b6d5762ddd Make berry tree animate the breaking after last life cycle, added LiteralHexColorAdapter type adapter 2022-12-05 13:47:10 +00:00
Licious17 dc0ab2e810 Add foliage color as a customizable datapack property of berries. 2022-12-05 13:27:18 +00:00
Licious17 f50ec2ae5a Fix berry tree not re-rolling growth points between harvests 2022-12-04 13:20:35 +00:00
Licious17 a4d0ddf229 Prevent unnecessary chunk saving on berry tree life cycles value update if the value is the same 2022-12-04 13:15:54 +00:00
Licious17 b1d84fef55 Fix berry tree entity read/write nbt being the other way around 2022-12-04 13:14:36 +00:00
Licious17 e26f4c33ba Document util functions inside BerryBlock and BerryBlockEntity 2022-12-03 14:21:09 +00:00
Licious17 7b023ab34f License stuff. 2022-12-03 14:15:57 +00:00
Licious17 ded186890d Implemented berry harvesting and life cycles. 2022-12-03 14:14:36 +00:00
Licious17 4c5b16e502 Added util to get the berry item and block implementation from the berry API, added pickstack functionality for the berry block 2022-12-03 12:35:15 +00:00
Licious17 6b95f8b7bf Update berry yield API for better mod compat 2022-12-03 12:24:44 +00:00
Licious17 4a50293e04 Change usages of VoxelShapes#cuboid to Block#createCuboidShape 2022-12-02 23:51:54 +00:00
Licious17 9fa829b30a Enabled dynamicBounds setting for Berry blocks 2022-12-02 23:07:50 +00:00
Licious17 0dc6b8321e Expanded berry growth factors to have API, allow data driven berry tree voxelshapes 2022-12-02 22:25:27 +00:00
Licious17 4616e61825 Implement berry datapack loading 2022-12-01 22:52:51 +00:00
Licious17 63c0b7da37 More berry data 2022-12-01 15:04:43 +00:00
Licious17 aad041998c Added block rendering stuff for berries 2022-11-30 13:22:52 +00:00
Licious17 b0e58975f2 License stuff, fixed berry tooltip 2022-11-30 12:41:00 +00:00
Licious17 cf41dd6fb4 Implemented Pecha interaction, fixed bad refactor on Flavor package path 2022-11-30 12:33:07 +00:00
Licious17 a66eb04097 Move InteractiveItem to API 2022-11-29 10:45:00 +00:00
Licious17 960d20599e Refactor InteractiveItem to be an interface instead, progress on berry block n item 2022-11-28 18:07:25 +00:00
Licious17 8ea6f13d34 License stuff 2022-11-28 15:21:03 +00:00
Licious17 34d28c798d Progress on data backing berries 2022-11-28 15:10:32 +00:00
Licious17 cb0046f4cc Fix merge bad import 2022-11-28 12:56:09 +00:00
Licious17 b475ead059 Merge branch 'main' into berry
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2022-11-28 12:52:36 +00:00
Licious17 9557c48cab Merge remote-tracking branch 'origin/berry' into berry 2022-11-28 12:50:26 +00:00
Licious17 1062507ffb Added barebones berry block 2022-11-28 12:50:15 +00:00
dhi_holo b5aef1cccc Revert "Update first_evolution.json"
This reverts commit f9df1085ec.
2022-11-26 03:27:20 +10:00
dhi_holo 658e6a9da5 Revert "Advancements Fixed Maybe?"
This reverts commit ff6f18d575.
2022-11-26 03:27:12 +10:00
dhi_holo ff6f18d575 Advancements Fixed Maybe?
I went and rewrote parts of the advancements until they worked properly as far as I can tell.
I don't get why it was breaking, but it was, and hopefully it won't from now on
2022-11-26 02:32:38 +10:00
dhi_holo f9df1085ec Update first_evolution.json
Should fix the issue Hiro mentioned earlier
2022-11-26 01:07:02 +10:00
Licious17 997bacefe2 Merge branch 'main' into berry 2022-11-25 12:46:45 +00:00
Licious17 5f94707a9e Added pecha berry assets, added pecha berry item 2022-11-25 12:40:47 +00:00
4256 changed files with 281638 additions and 22925 deletions
+144 -34
View File
@@ -8,32 +8,53 @@
- Added big roots, generating from cave ceiling dirt and sometimes spreading into energy roots.
- Added trading between players. Press R while looking at another player, you'll figure it out.
- Added pasture blocks, used to let your PC Pokémon roam around your base.
- Added 69 berry trees. Some are found in village farms, some from planting different berries close to each other.
- Added medicine brewing using medicinal leeks and berries in brewing stands.
- Added Pokémon cries when in battles and being sent out.
- Added support for scale in animations.
- Added support for jump keyframes (i.e. pre and post keyframes)
- Made Nosepass point towards world spawn while idle.
- Added AI for Nosepass to point towards world spawn when idle.
- Added structure spawn conditions
- Added Pokémon cry system for battles and sending out.
- Added cries for Gen 1-9 starters and their evolutions.
- Added cries for Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Cleffa, Clefairy, Clefable, Tauros, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, Health Feather, Muscle Feather, Resist Feather, Genius Feather, Clever Feather, Genius Feather
- Added recipes for Berry Juice, Heal Powder, Remedy, Fine Remedy, Superb Remedy, Revive, Max Revive, HP Up, Protein, Iron, Calcium, Zinc, Carbos, PP Up, PP Max, Pasture, Medicinal Leek to Magenta Dye, Roasted Leek, Leek and Potato Stew, Braised Vivichoke, Vivichoke Dip, Mulch Base, Growth Mulch, Surprise Mulch, Coarse Mulch, Humid Mulch, Rich Mulch, Loamy Mulch, Peat Mulch, Sandy Mulch, X Attack, X Defense, X Special Attack, X Special Defense, X Speed, X Accuracy, Dire Hit, Guard Spec, Power Anklet, Power Band, Power Belt, Power Bracelet, Power Lens, Power Weight, Bright Powder, Destiny Knot
- Added cries for Farfetchd, Galarian Farfetched, Rookidee, Corvisquire, Corviknight, Caterpie, Metapod, Butterfree, Weedle, Kakuna, Beedrill, Pidgey, Pidgeotto, Pidgeot, Rattata, Raticate, Clefairy, Clefable, Tauros, Eevee, Vaporeon, Flareon, Jolteon, Chatot, Darmanitan, Darumaka, Lucario, Mimikyu, Quagsire, Riolu, Wooper, Cleffa, Miltank, Buneary, Lopunny, Wooloo, Dubwool.
- Added a shoulder mount for Mimikyu
- Added Advancement trigger for defeating Pokémon and collecting varieties of Pokémon.
- Added sleep animations to Arcanine, Jigglypuff, Wigglytuff, Vulpix and Ninetales.
- Added flying placeholder animations to Pidgey, Pidgeotto, Pidgeot, Golbat, Crobat, Scyther, Scizor, Zapdos, Moltres, Articuno, Dragonite, Rowlet, Dartrix, and Decidueye.
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest)
- Added loot to various vanilla chest loot tables (Link Cable in Ancient Cities, Woodland Mansions, End Cities, and Igloos, Vivichoke Seeds in Jungle Temples, Dungeons, and Plains, Savanna, Snowy, and Taiga Villages, and all 7 Apricorn Sprouts in Desert, Plains, Savanna, Snowy, and Taiga Villages, as well as the Bonus Chest, which can also have 5 of the basic Poké Ball types)
- Added the `/teststore <player> <store> <properties>` command allowing command block/mcfunction users to query a party, PC or both for Pokémon matching specific properties and returning the match count, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.teststore` if a permission mod is present.
- Added the `/querylearnset <player> <slot> <move>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon can learn a specific move returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.querylearnset` if a permission mod is present.
- Added the `/testpcslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a pc slot and check if the Pokémon matches specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpcslot` if a permission mod is present.
- Added the `/testpartyslot <player> <slot> <properties>` command allowing command block/mcfunction users to query a party slot and check if the Pokémon matches a specific properties returning a 1 if yes otherwise 0, this will be a cheat command in the Minecraft permission system or use the permission `cobblemon.command.testpartyslot` if a permission mod is present.
- Added the `/clearparty <player>` command for emptying a player's party.
- Added the `/clearpc <player>` command for emptying a player's PC.
- Added the `/pokemonrestart <reset_starters>` and the `/pokemonrestartother <player> <reset_starters>` command allowing command block/mcfunction users to reset a players Pokémon data.
- Added support for "isBattle" and "isTouchingWater" properties on resource pack Pokémon poses. This allows your custom Pokémon to be posed differently when in battle.
- Added support for "isVisible" on a transformed part on resource pack Pokémon poses. This allows your custom Pokémon to have bones disappear in specific poses, such as hiding Greninja's throwing star when not in a battle pose.
- Added a "doShinyStarters" gamerule
- Added the `doPokemonLoot` gamerule to toggle Pokémon dropping items/exp on death.
- Added support for battle music. Sounds can be added to the ``battle.pvp.default`` and ``battle.pvw.default`` sound events.
- Added ability activation announcement when in battle.
- Added Auspicious Armor and Malicious Armor, which can be used to evolve Charcadet into Armarouge or Ceruledge respectively.
- Added animations for Wailord.
- Added Cherry Torterra variant.
- Added 2 new face spots for Spinda.
- Added Forretress Shulker variant.
- Added 'enabled' optional property on model layers, allowing later variations to disable previously-defined layers. See [this issue](https://gitlab.com/cable-mc/cobblemon/-/issues/335) for how this looks.
### Pokémon Added
#### Gen 2
- Chikorita
- Bayleef
- Meganium
- Totodile
- Croconaw
- Feraligatr
- Cyndaquil
- Quilava
- Typhlosion
- Spinarak
- Ariados
- Shuckle
@@ -47,6 +68,12 @@
- Flaaffy
- Ampharos
- Sudowoodo
- Snubbull
- Granbull
- Phanpy
- Donphan
- Teddiursa
- Ursaring
#### Gen 3
@@ -73,11 +100,22 @@
- Chimecho
- Barboach
- Whiscash
- Volbeat
- Illumise
- Zigzagoon
- Linoone
- Ralts
- Kirlia
- Gardevoir
- Nincada
- Ninjask
- Shedinja
- Beldum
- Metang
- Metagross
#### Gen 4
- Cherubi (Planned)
- Cherrim (Planned)
- Carnivine
- Shinx
- Luxio
@@ -90,6 +128,12 @@
- Chatot
- Combee
- Vespiquen
- Buizel
- Floatzel
- Starly
- Staravia
- Staraptor
- Gallade
#### Gen 5
@@ -100,38 +144,59 @@
- Venepede
- Whirlpede
- Scolipede
- Yamask (Planned)
- Cofagrigus (Planned)
- Patrat (Planned)
- Watchog (Planned)
- Yamask
- Cofagrigus
- Patrat
- Watchog
- Lillipup
- Herdier
- Stoutland
- Cottonee
- Whimsicott
- Pidove
- Tranquill
- Unfezant
- Timburr
- Gurdurr
- Conkeldurr
#### Gen 6
- Scatterbug
- Spewpa
- Vivillion
- Skrelp (Planned)
- Dragalge (Planned)
- Bunnelby (Planned)
- Diggersby (Planned)
- Skrelp
- Dragalge
- Bunnelby
- Diggersby
- Phantump
- Trevenant
- Fletchling
- Fletchinder
- Talonflame
#### Gen 7
- Wishiwashi (Planned)
- Wishiwashi
- Cutiefly
- Ribombee
- Stufful
- Bewear
- Comfey
- Alolan Exeggutor
- Alolan Raichu
- Alolan Meowth
- Alolan Persian
- Komala
- Wimpod
- Golisopod
- Crabrawler
- Crabominable
#### Gen 8
- Arrokuda (Planned)
- Barraskewda (Planned)
- Arrokuda
- Barraskewda
- Nickit
- Thievul
- Falinks
@@ -140,12 +205,28 @@
- Rookidee
- Corvisquire
- Corviknight
- Galarian Ponyta
- Galarian Rapidash
- Yamper
- Boltund
- Galarian Zigzagoon
- Galarian Linoone
- Obstagoon
- Galarian Meowth
- Perrserker
- Ursaluna
#### Gen 9
- Sprigatitto
- Floragato
- Meowscarada
- Fuecoco
- Crocalor
- Skeledirge
- Quaxly
- Quaxwell
- Quaquaval
- Flittle
- Espathra
- Garganacl
@@ -155,20 +236,29 @@
- Ceruledge
- Cetoddle
- Cetitan
- Shroodle (Planned)
- Grafaiai (Planned)
- Shroodle
- Grafaiai
- Tandemaus
- Maushold
- Varoom
- Revavroom
- Squawkabilly (Planned)
- Squawkabilly
- Glimmet
- Glimmora
- Annihilape
- Tinkatink
- Tinkatuff
- Tinkaton
- Maschiff
- Mabosstiff
- Lechonk
- Oinkologne
- Paldean Wooper
- Clodsire
### Changes
- Updated model and textures of Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, Gengar, Wooper Drowzee, Hypno, Aerodactyl, Spearow, Fearow, Lickitung, Pidgeotto, Pidgeot, Scyther, Scizor, Kleavor, Popplio, Brionne, Primarina, Torchic, Combusken, Blaziken, Aerodacyl, Scyther, Scizor, Kleavor, Lickitung, Lickilicky, Happiny, Chansey, Pidgey, Pidgeotto, Pidgeot, Spearow, Fearow, Drowzee, Hypno, Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Farfetchd, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, and Gengar.
- Updated models of Sceptile and Shuckle.
- Updated model and textures of Spiritomb, Koffing, Weezing, Kadabra, Alakazam, Emolga, Oshawott, Doduo, Dodrio, Dratini, Dragonair, Dragonite, Sneasel, Weavile, Gyarados, Hitmonlee, Hitmonchan, Chesnaught, Spinda, Mamoswine, Steelix, Midreavus, Mismagius, Buneary, Lopunny, Golduck, Meowth, Persian, Fennekin, Braixen, Delphox, Snivy, Servine, Serperior, Ratatta, Raticate, Nidorina, Nidoqueen, NidoranM, Nidoking, Riolu, Lucario, Haunter, Mankey, Primeape, Mew, Mewtwo, Arcanine, Magnemite, Magneton, Magnezone, Exeggcute, Exeggutor, Elekid, Electabuzz, Electivire, Pichu, Pikachu, Raichu, Gengar, Wooper, Drowzee, Hypno, Aerodactyl, Spearow, Fearow, Lickitung, Pidgey, Pidgeotto, Pidgeot, Scyther, Scizor, Kleavor, Popplio, Brionne, Primarina, Torchic, Combusken, Blaziken, Aerodacyl, Scyther, Scizor, Kleavor, Lickitung, Lickilicky, Happiny, Chansey and Blissey.
- Updated animations for Steelix, Turtwig, Grotle, Torterra, Ponyta, Rapidash, Piplup, Prinplup, Empoleon, Drowzee, Hypno, Farfetch'd, Exeggcute, Exeggutor, Bidoof, Chimecho, Lickytung, Lickilicky, Popplio, Brionne, Luvdisc, Chimchar, Monferno, Infernape, Sobble, Drizzile, Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad, Lombre, Ludicolo, Pumpkaboo, Gourgeist.
- Updated sprites for EV medicines, the rare candy, and the apricorn door item.
- Updated textures for apricorn doors and all the evolution stone ores.
- Updated Apricorn Leaves color.
@@ -180,23 +270,30 @@
- Cobblemon items can now all have their own tooltips via resourcepacks, to add a tooltip simply add a lang entry like "item.cobblemon.{item_id}.tooltip", if you want to add multiple tooltip lines you can do so with "item.cobblemon.{item_id}.tooltip_1" and upwards.
- Updated texture of Weedle.
- Updated shiny texture of Dwebble and Crustle.
- Updated Animations for Piplup, Prinplup, Empoleon, Drowzee, Hypno, Farfetch'd, Exeggcute, Exeggutor, Bidoof, Chimecho, Lickytung, Lickilicky, Popplio, Brionne, Luvdisc, Chimchar, Monferno, Infernape, Sobble, Drizzile, Inteleon, Greninja, Heatmor, Aerodactyl, Ditto, Lotad, Lombre, Ludicolo, Pumpkaboo, Gourgeist.
- Reorganised the advancements recipes folder
- Pokeedit command now supports IVs and EVs.
- Pokéedit command now supports IVs and EVs.
- Reorganised creative categories
- Pokemon can now wander into non-solid blocks such as foliage
- Pokeballs now despawn after 30 seconds
- Pokémon can now wander into non-solid blocks such as foliage
- Pokéballs now despawn after 30 seconds
- Dive Balls will now have the same motion speed underwater as if they were thrown in the air.
- Hardcoded potion shoulder effects have been removed, you can now use any potion vanilla or otherwise with the parameters you'd like, for more information see the [Datapack & Resourcepack Creators](#datapack-&-resourcepack-creators) section.
- Clicking categories of the Stat sub-section or the party reorder button in the Summary screen will now produce a click sound.
- Updated PC Recipe.
- Improved Pokémon AI and movement.
- Friendship will slowly increase when Pokémon are shoulder-mounted.
- Master balls are now unable to be burned when dropped into fire/lava.
- Pokémon will appear red when hurt, like regular entities, except when they're fainting.
- Ponyta and Rapidash now have animated textures.
- Pokémon's air meter no longer depletes while battling underwater.
- Sleeping partially restores PP of Pokémon
- Item interaction evolutions and held item requirements now support NBT by creating an object JSON containing the key ``item`` for what used to be the existing condition support and a ``nbt`` key for the NBT format, this is the string [format](https://minecraft.fandom.com/wiki/NBT_format) expected in commands. Existing data does not need to be updated.
- Shoulder mounts now match the shoulder position a bit more accurately when sneaking.
- Poison Heal will now cause poisoned Pokémon to heal outside of battle.
### Fixes
- Fixed spawning moon phase dependent Pokémon only when the moon phase is wrong
- Fixed large Pokémon spawning partially inside walls where they suffocate.
- Fixed messages for entry hazards, screens, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye and Safeguard.
- Fixed messages for entry hazards, screens, weather, damage, healing, Tailwind, Perish Song, Destiny Bond, Shed Skin, Uproar, Forewarn, Disguise, Arena Trap, Yawn, Curse, Clamp, Whirlpool, Liquid Ooze, Miracle Eye, Safeguard, Magic Bounce, Lock On, Focus Energy, Confusion, and more.
- Fixed Porygon not evolving with an Upgrade.
- Fixed super sized Pumpkaboo not having any moves.
- Fixed Infernape look animation.
@@ -222,18 +319,30 @@
- Fixed particle animations not running when a Pokémon is off-screen.
- Fixed Pokémon variants and layers not rendering correctly when shouldered and playing on a dedicated server, existing shoulders affected will need to be retrieved and shouldered again.
- Shoulder effects will now stay applied through situations that remove potion effects such as drinking milk.
- Fixed Shedninja evolving to use the consumed Pokeball and removed the held item.
- Fixed Shedinja evolving to use the consumed Pokeball and removed the held item.
- Fixed non-existent species in spawn pool file causing random species to spawn.
- Fixed shearing Pokémon dropping 0-2 wool instead of 1-3.
- Fixed some alignment issues in the stat hexagon of the summary menu.
- Fixed capture calculations not applying ball bonuses entirely correctly.
- Fixed Shedinja healing above 1 HP.
- Fixed battles soft-locking when consecutive Pokémon faint on switch-in.
- Fixed timing and color of battle window messages.
- Fixed players being able to trade, battle and let out their Pokémon while in spectator mode.
- Fixed Galarian Yamask not being able to evolve and by proxy the ``damage_taken`` evolution requirement.
- Fixed Bisharp not being able to evolve and by proxy the ``defeat`` evolution requirement.
- Fixed White-Striped Basculin not being able to evolve and by proxy the ``recoil`` evolution requirement.
- Fixed Primeape, Qwilfish and Stantler not being able to evolve and by proxy the ``use_move`` evolution requirement.
- Fixed Bramblin, Pawmo, and Rellor not being able to evolve and by proxy the ``blocks_traveled`` evolution requirement.
- Fixed displayName property in spawn files not doing what it's meant to do.
### Developer
- Added SpawnEvent
- Added persistent NBT property inside Pokemon to store quick and simple data.
- Added SpawnEvent, ThrownPokeballHitEvent, PokemonSentEvent, PokemonRecalledEvent.
- Added BattleFledEvent, BattleStartedEvent, BattleFaintedEvent.
- Added persistent NBT property inside Pokémon to store quick and simple data.
- Species and FormData have had their evolutions, pre-evolution and labels properties exposed. It is still recommended to work using a Pokémon instance when possible.
- Added capture check to BattleVictoryEvent
- Added ThownPokeballHitEvent
- Added capture check to BattleVictoryEvent.
- The various hardcoded potion shoulder effects have been removed, make use of PotionBaseEffect.
- Added ContextManager for tracking causes and contexts of conditions created during a battle. See BattleContext for types of conditions that are tracked.
### Datapack & Resourcepack Creators
- All potion related shoulder effects have had their IDs changed, they now all share the same type being `potion_effect` and use the vanilla Potion data [parameters](https://minecraft.fandom.com/wiki/Potion#Item_data), take for example the default Pidgey asset:
@@ -247,6 +356,7 @@
"showIcon": false
}
```
- Renamed the ``walked_steps`` evolution requirement to ``blocks_traveled``.
## [1.3.1 (March 31st, 2023)](#1-3-1)
@@ -1,3 +1,13 @@
/*
*
* * 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/.
*
*/
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
val accessWidenerFile = "src/main/resources/cobblemon-common.accesswidener"
@@ -13,6 +23,8 @@ plugins {
repositories {
mavenCentral()
//JEI
maven("https://maven.blamejared.com/")
}
license {
@@ -55,4 +67,4 @@ tasks {
withType<Jar> {
from(rootProject.file("LICENSE"))
}
}
}
+11
View File
@@ -1,3 +1,13 @@
/*
*
* * 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/.
*
*/
plugins {
id("cobblemon.base-conventions")
id("cobblemon.publish-conventions")
@@ -18,6 +28,7 @@ dependencies {
modImplementation(libs.fabricLoader)
modApi(libs.molang)
compileOnlyApi(libs.jeiApi)
// For Showdown
modCompileOnly(libs.graal)
@@ -0,0 +1,16 @@
/*
* 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.bridges;
import net.minecraft.structure.processor.StructureProcessor;
public interface StructureProcessorListBridge {
void append(StructureProcessor processor);
void clear();
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.client.render.models.blockbench.pose;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.entity.Entity;
@@ -38,7 +40,7 @@ public interface Bone {
/**
* Renders a particular bone in relation to a particular entity.
*
* @param entity The entity this bone is being drawn for. This parameter need not be available and can be null
* @param context The render context this bone is being drawn for.
* @param stack The current [MatrixStack] for the renderer
* @param buffer The vertex consumer deployed by the renderer
* @param packedLight The light level to be applied to the bone
@@ -50,8 +52,8 @@ public interface Bone {
*
* @since 1.4.0
*/
<T extends Entity> void render(
@Nullable T entity,
void render(
RenderContext context,
MatrixStack stack,
VertexConsumer buffer,
int packedLight,
@@ -0,0 +1,44 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.block.PreEmptsExplosion;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import it.unimi.dsi.fastutil.objects.ObjectListIterator;
import net.minecraft.block.BlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraft.world.explosion.Explosion;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(value = Explosion.class)
public class ExplosionMixin {
@Shadow @Final private World world;
@Inject(
method = "affectWorld",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/block/BlockState;getBlock()Lnet/minecraft/block/Block;"
),
locals = LocalCapture.CAPTURE_FAILSOFT
)
public void cobblemon$whenExploded(boolean particles, CallbackInfo ci, boolean bl, ObjectArrayList<?> objectArrayList, boolean bl2, ObjectListIterator<?> var5, BlockPos blockPos, BlockState blockState) {
if (blockState.getBlock() instanceof PreEmptsExplosion preExplosionBlock) {
preExplosionBlock.whenExploded(world, blockState, blockPos);
}
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.api.tags.CobblemonBiomeTags;
import com.cobblemon.mod.common.music.CustomMusic;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.registry.entry.RegistryEntry;
import net.minecraft.sound.MusicSound;
import net.minecraft.world.biome.Biome;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
@Mixin(MinecraftClient.class)
public class MinecraftClientMixin {
@Shadow
public ClientPlayerEntity player;
@Inject(method = "getMusicType", at = @At("RETURN"), cancellable = true)
private void cobblemon$getMusicType(CallbackInfoReturnable<MusicSound> cir) {
if (this.player != null) {
RegistryEntry<Biome> playerBiome = this.player.getWorld().getBiome(this.player.getBlockPos());
List<MusicSound> possibleTracks = new ArrayList<>();
possibleTracks.add(cir.getReturnValue());
// Custom BiomeTag check to add music to certain biomes
if (playerBiome.isIn(CobblemonBiomeTags.IS_ABYSS)) {
possibleTracks.add(CustomMusic.DEEP_DARK);
}
cir.setReturnValue(possibleTracks.get(new Random().nextInt(possibleTracks.size())));
}
}
}
@@ -8,7 +8,9 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityModel;
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone;
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext;
import net.minecraft.client.model.ModelPart;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
@@ -36,7 +38,7 @@ public abstract class ModelPartMixin implements Bone {
}
@Override
public <T extends Entity> void render(T entity, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
public void render(RenderContext context, MatrixStack stack, VertexConsumer buffer, int packedLight, int packedOverlay, float r, float g, float b, float a) {
this.render(stack, buffer, packedLight, packedOverlay, r, g, b, a);
}
@@ -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;
import com.mojang.datafixers.util.Pair;
import it.unimi.dsi.fastutil.objects.ObjectArrayList;
import net.minecraft.structure.pool.StructurePool;
import net.minecraft.structure.pool.StructurePoolElement;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
import java.util.List;
@Mixin(StructurePool.class)
public interface StructurePoolAccessor {
@Accessor("elementCounts")
List<Pair<StructurePoolElement, Integer>> getElementCounts();
@Mutable
@Accessor("elementCounts")
void setElementCounts(List<Pair<StructurePoolElement, Integer>> elementCounts);
@Accessor("elements")
ObjectArrayList<StructurePoolElement> getElements();
}
@@ -0,0 +1,217 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.world.CobblemonStructureIDs;
import net.minecraft.registry.Registry;
import net.minecraft.structure.PoolStructurePiece;
import net.minecraft.structure.StructureTemplateManager;
import net.minecraft.structure.pool.*;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.random.Random;
import net.minecraft.util.shape.VoxelShape;
import net.minecraft.world.HeightLimitView;
import net.minecraft.world.gen.chunk.ChunkGenerator;
import net.minecraft.world.gen.noise.NoiseConfig;
import org.apache.commons.lang3.mutable.MutableObject;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.*;
@Mixin(StructurePoolBasedGenerator.StructurePoolGenerator.class)
public abstract class StructurePoolGeneratorMixin {
@Final
@Mutable
@Shadow Deque<StructurePoolBasedGenerator.ShapedPoolStructurePiece> structurePieces;
Map<String, Integer> generatedStructureGroupCounts;
private static final Map<String, Integer> structureMaxes;
private static final Map<Identifier, Set<String>> structureGroups = new HashMap<>();
static {
String pokecenter = "pokecenter";
String berry = "berry_farm";
structureGroups.put(CobblemonStructureIDs.PLAINS_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.DESERT_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SNOWY_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.TAIGA_POKECENTER, Set.of(pokecenter));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SAVANNA_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.PLAINS_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.SNOWY_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.DESERT_BERRY_LARGE, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_SMALL, Set.of(berry));
structureGroups.put(CobblemonStructureIDs.TAIGA_BERRY_LARGE, Set.of(berry));
//Mapped using location string as key
Map<String, Integer> aMap = new HashMap<>();
aMap.put(pokecenter, 1);
aMap.put(berry, 2);
// aMap.put(CobblemonStructureIDs.PLAINS_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.DESERT_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.SAVANNA_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.SNOWY_LONG_PATH, 3);
// aMap.put(CobblemonStructureIDs.TAIGA_LONG_PATH, 3);
structureMaxes = Collections.unmodifiableMap(aMap);
}
public Set<String> getGroups(Identifier structureIdentifier) {
return structureGroups.getOrDefault(structureIdentifier, Set.of());
}
public boolean hasReachedMaximum(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
int count = generatedStructureGroupCounts.getOrDefault(group, 0);
if (count >= structureMaxes.getOrDefault(group, Integer.MAX_VALUE)) {
return true;
}
}
return false;
}
public void incrementStructureCount(Identifier structureIdentifier) {
Set<String> groups = getGroups(structureIdentifier);
for (String group : groups) {
generatedStructureGroupCounts.put(group, generatedStructureGroupCounts.getOrDefault(group, 0) + 1);
}
}
@Inject(method = "<init>", at = @At("RETURN"))
private void onStructurePoolGeneratorCreation(Registry<StructurePool> registry, int maxSize, ChunkGenerator chunkGenerator, StructureTemplateManager structureTemplateManager, List<? super PoolStructurePiece> children, Random random, CallbackInfo ci) {
generatedStructureGroupCounts = new HashMap<>();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private Iterator<StructurePoolElement> reduceStructurePoolElementIterator(Iterator<StructurePoolElement> iterator) {
List<StructurePoolElement> reducedList = new ArrayList<>();
while (iterator.hasNext()) {
StructurePoolElement structure = iterator.next();
Identifier structurePieceLocationKey = getCobblemonOnlyLocation(structure);
if (structurePieceLocationKey == null) {
reducedList.add(structure);
continue;
}
if (!hasReachedMaximum(structurePieceLocationKey)) {
reducedList.add(structure);
}
}
return reducedList.iterator();
}
@ModifyVariable(method = "generatePiece", at = @At("STORE"), ordinal = 1)
private PoolStructurePiece injected(PoolStructurePiece poolStructurePiece) {
Identifier structureLocationKey = getCobblemonOnlyLocation(poolStructurePiece.getPoolElement());
if (structureLocationKey != null) {
incrementStructureCount(structureLocationKey);
}
return poolStructurePiece;
}
// This doesn't SEEM necessary? It appears to work without.
@Inject(method = "generatePiece", at = @At("HEAD"))
private void beforeGeneratePiece(PoolStructurePiece piece, MutableObject<VoxelShape> pieceShape, int minY, boolean modifyBoundingBox, HeightLimitView world, NoiseConfig noiseConfig, CallbackInfo ci) {
// Identifier structureLocationKey = getCobblemonOnlyLocation(piece.getPoolElement());
//
// if (structureLocationKey != null) {
// Integer currentlyGenerated = generatedStructureCounts.get(structureLocationKey);
// if (currentlyGenerated == null) currentlyGenerated = 0;
// generatedStructureCounts.put(structureLocationKey, currentlyGenerated + 1);
// }
//
// List<StructurePoolBasedGenerator.ShapedPoolStructurePiece> reducedStructurePiecesList = structurePieces.stream().toList();
//
// for (Identifier maxStructureLocationKey : structureMaxes.keySet()) {
// Integer maxAllowed = structureMaxes.get(maxStructureLocationKey);
//
// Integer currentlyGenerated = generatedStructureCounts.get(maxStructureLocationKey);
// if (currentlyGenerated == null) currentlyGenerated = 0;
// if (currentlyGenerated < maxAllowed) {
// continue;
// }
//
// //Already have max so need to remove
// reducedStructurePiecesList = reducedStructurePiecesList.stream()
// .filter(shapedStructurePiece -> {
// Identifier locationKey = getCobblemonOnlyLocation(shapedStructurePiece.piece.getPoolElement());
// if (locationKey == null) {
// return true;
// }
//
// return !locationKey.equals(maxStructureLocationKey);
// })
// .collect(Collectors.toList());
// }
//
// structurePieces = new ArrayDeque<>(reducedStructurePiecesList);
}
private static Identifier getCobblemonOnlyLocation(StructurePoolElement structurePoolElement) {
Identifier location = getLocationIfAvailable(structurePoolElement);
if (location == null) return null;
if (!location.getNamespace().equals("cobblemon")) return null;
return location;
}
private static Identifier getLocationIfAvailable(StructurePoolElement structurePoolElement) {
if (structurePoolElement instanceof LegacySinglePoolElement legacySinglePoolElement) {
if (legacySinglePoolElement.location.left().isEmpty()) return null;
return legacySinglePoolElement.location.left().get();
} else if (structurePoolElement instanceof SinglePoolElement singlePoolElement) {
if (singlePoolElement.location.left().isEmpty()) return null;
return singlePoolElement.location.left().get();
} else {
return null;
}
}
// private List<StructurePoolElement> removeInstanceOfLocationKeyFrom(List<StructurePoolElement> structureList, Integer allowedNumberOfInstances, Identifier locationKey) {
// List<StructurePoolElement> reducedList = new ArrayList<>();
// int instancesFound = 0;
//
// for (StructurePoolElement structurePoolElement: structureList) {
// Identifier structureKey = getCobblemonOnlyLocation(structurePoolElement);
// if (structureKey == null || !structureKey.equals(locationKey)) {
// reducedList.add(structurePoolElement);
// continue;
// }
//
// if (instancesFound >= allowedNumberOfInstances) {
// continue;
// }
//
// reducedList.add(structurePoolElement);
// instancesFound++;
// }
//
// return reducedList;
// }
}
@@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.bridges.StructureProcessorListBridge;
import com.google.common.collect.ImmutableList;
import java.util.ArrayList;
import java.util.List;
import net.minecraft.structure.processor.StructureProcessor;
import net.minecraft.structure.processor.StructureProcessorList;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.Shadow;
@Mixin(StructureProcessorList.class)
public class StructureProcessorListMixin implements StructureProcessorListBridge {
@Final
@Mutable
@Shadow
private List<StructureProcessor> list;
@Override
public void append(StructureProcessor processor) {
List<StructureProcessor> mutable = new ArrayList<>(this.list);
mutable.add(processor);
this.list = ImmutableList.copyOf(mutable);
}
@Override
public void clear() {
this.list = ImmutableList.of();
}
}
@@ -18,7 +18,9 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
@@ -61,12 +63,7 @@ import com.cobblemon.mod.common.battles.BattleSide
import com.cobblemon.mod.common.battles.ShowdownThread
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.command.argument.MoveArgumentType
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.command.argument.PokemonArgumentType
import com.cobblemon.mod.common.command.argument.PokemonPropertiesArgumentType
import com.cobblemon.mod.common.command.argument.PokemonStoreArgumentType
import com.cobblemon.mod.common.command.argument.SpawnBucketArgumentType
import com.cobblemon.mod.common.command.argument.*
import com.cobblemon.mod.common.config.CobblemonConfig
import com.cobblemon.mod.common.config.LastChangedVersion
import com.cobblemon.mod.common.config.constraint.IntConstraint
@@ -91,25 +88,10 @@ import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.starter.CobblemonStarterHandler
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.ifDedicatedServer
import com.cobblemon.mod.common.util.isLaterVersion
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.removeAmountIf
import com.cobblemon.mod.common.util.server
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.world.feature.CobblemonPlacedFeatures
import com.cobblemon.mod.common.world.feature.ore.CobblemonOrePlacedFeatures
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.UUID
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
import net.minecraft.client.MinecraftClient
import net.minecraft.command.argument.serialize.ConstantArgumentSerializer
import net.minecraft.entity.data.TrackedDataHandlerRegistry
@@ -119,6 +101,15 @@ import net.minecraft.registry.RegistryKey
import net.minecraft.util.WorldSavePath
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
import java.io.File
import java.io.FileReader
import java.io.FileWriter
import java.io.PrintWriter
import java.util.*
import kotlin.properties.Delegates
import kotlin.reflect.full.memberProperties
import kotlin.reflect.jvm.isAccessible
import kotlin.reflect.jvm.javaField
object Cobblemon {
const val MODID = "cobblemon"
@@ -312,6 +303,8 @@ object Cobblemon {
}
}
}
LEVEL_UP_EVENT.subscribe { AdvancementHandler.onLevelUp(it) }
TRADE_COMPLETED.subscribe { AdvancementHandler.onTradeCompleted(it) }
BagItems.observable.subscribe {
LOGGER.info("Starting dummy Showdown battle to force it to pre-load data.")
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.block.entity.HealingMachineBlockEntity
import com.cobblemon.mod.common.block.entity.PCBlockEntity
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
@@ -27,6 +28,7 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val HEALING_MACHINE: BlockEntityType<HealingMachineBlockEntity> = this.create("healing_machine", BlockEntityType.Builder.create(::HealingMachineBlockEntity, CobblemonBlocks.HEALING_MACHINE).build(null))
@JvmField
val PC: BlockEntityType<PCBlockEntity> = this.create("pc", BlockEntityType.Builder.create(::PCBlockEntity, CobblemonBlocks.PC).build(null))
val BERRY = this.create("berry", BlockEntityType.Builder.create(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@JvmField
val PASTURE: BlockEntityType<PokemonPastureBlockEntity> = this.create("pasture", BlockEntityType.Builder.create(::PokemonPastureBlockEntity, CobblemonBlocks.PASTURE).build(null))
@@ -17,6 +17,8 @@ import com.cobblemon.mod.common.mixin.invoker.StairsBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.TrapdoorBlockInvoker
import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.*
import net.minecraft.block.piston.PistonBehavior
import net.minecraft.entity.effect.StatusEffects
@@ -25,6 +27,7 @@ import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.util.Identifier
import net.minecraft.util.math.intprovider.UniformIntProvider
@Suppress("SameParameterValue", "HasPlatformType", "MemberVisibilityCanBePrivate", "unused")
@@ -227,6 +230,87 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
private val berries = mutableMapOf<Identifier, BerryBlock>()
val AGUAV_BERRY = this.berryBlock("aguav")
val APICOT_BERRY = this.berryBlock("apicot")
val ASPEAR_BERRY = this.berryBlock("aspear")
val BABIRI_BERRY = this.berryBlock("babiri")
val BELUE_BERRY = this.berryBlock("belue")
val BLUK_BERRY = this.berryBlock("bluk")
val CHARTI_BERRY = this.berryBlock("charti")
val CHERI_BERRY = this.berryBlock("cheri")
val CHESTO_BERRY = this.berryBlock("chesto")
val CHILAN_BERRY = this.berryBlock("chilan")
val CHOPLE_BERRY = this.berryBlock("chople")
val COBA_BERRY = this.berryBlock("coba")
val COLBUR_BERRY = this.berryBlock("colbur")
val CORNN_BERRY = this.berryBlock("cornn")
val CUSTAP_BERRY = this.berryBlock("custap")
val DURIN_BERRY = this.berryBlock("durin")
val ENIGMA_BERRY = this.berryBlock("enigma")
val FIGY_BERRY = this.berryBlock("figy")
val GANLON_BERRY = this.berryBlock("ganlon")
val GREPA_BERRY = this.berryBlock("grepa")
val HABAN_BERRY = this.berryBlock("haban")
val HONDEW_BERRY = this.berryBlock("hondew")
val HOPO_BERRY = this.berryBlock("hopo")
val IAPAPA_BERRY = this.berryBlock("iapapa")
val JABOCA_BERRY = this.berryBlock("jaboca")
val KASIB_BERRY = this.berryBlock("kasib")
val KEBIA_BERRY = this.berryBlock("kebia")
val KEE_BERRY = this.berryBlock("kee")
val KELPSY_BERRY = this.berryBlock("kelpsy")
val LANSAT_BERRY = this.berryBlock("lansat")
val LEPPA_BERRY = this.berryBlock("leppa")
val LIECHI_BERRY = this.berryBlock("liechi")
val LUM_BERRY = this.berryBlock("lum")
val MAGO_BERRY = this.berryBlock("mago")
val MAGOST_BERRY = this.berryBlock("magost")
val MARANGA_BERRY = this.berryBlock("maranga")
val MICLE_BERRY = this.berryBlock("micle")
val NANAB_BERRY = this.berryBlock("nanab")
val NOMEL_BERRY = this.berryBlock("nomel")
val OCCA_BERRY = this.berryBlock("occa")
val ORAN_BERRY = this.berryBlock("oran")
val PAMTRE_BERRY = this.berryBlock("pamtre")
val PASSHO_BERRY = this.berryBlock("passho")
val PAYAPA_BERRY = this.berryBlock("payapa")
val PECHA_BERRY = this.berryBlock("pecha")
val PERSIM_BERRY = this.berryBlock("persim")
val PETAYA_BERRY = this.berryBlock("petaya")
val PINAP_BERRY = this.berryBlock("pinap")
val POMEG_BERRY = this.berryBlock("pomeg")
val QUALOT_BERRY = this.berryBlock("qualot")
val RABUTA_BERRY = this.berryBlock("rabuta")
val RAWST_BERRY = this.berryBlock("rawst")
val RAZZ_BERRY = this.berryBlock("razz")
val RINDO_BERRY = this.berryBlock("rindo")
val ROSELI_BERRY = this.berryBlock("roseli")
val ROWAP_BERRY = this.berryBlock("rowap")
val SALAC_BERRY = this.berryBlock("salac")
val SHUCA_BERRY = this.berryBlock("shuca")
val SITRUS_BERRY = this.berryBlock("sitrus")
val SPELON_BERRY = this.berryBlock("spelon")
val STARF_BERRY = this.berryBlock("starf")
val TAMATO_BERRY = this.berryBlock("tamato")
val TANGA_BERRY = this.berryBlock("tanga")
val TOUGA_BERRY = this.berryBlock("touga")
val WACAN_BERRY = this.berryBlock("wacan")
val WATMEL_BERRY = this.berryBlock("watmel")
val WEPEAR_BERRY = this.berryBlock("wepear")
val WIKI_BERRY = this.berryBlock("wiki")
val YACHE_BERRY = this.berryBlock("yache")
fun berries() = this.berries.toMap()
private fun berryBlock(name: String): BerryBlock {
val identifier = cobblemonResource("${name}_berry")
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP)))
this.berries[identifier] = block
return block
}
/**
* Helper method for creating logs
* copied over from Vanilla
@@ -248,4 +332,4 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val block = BlocksInvoker.createLeavesBlock(BlockSoundGroup.GRASS)
return this.create(name, block)
}
}
}
@@ -44,6 +44,7 @@ object CobblemonCommands {
TestPcSlotCommand.register(dispatcher)
TestPartySlotCommand.register(dispatcher)
ClearPartyCommand.register(dispatcher)
ClearPCCommand.register(dispatcher)
PokemonRestartCommand.register(dispatcher)
}
}
@@ -13,18 +13,27 @@ import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.MintBlock.MintType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
import com.cobblemon.mod.common.item.berry.FriendshipRaisingBerryItem
import com.cobblemon.mod.common.item.berry.HealingBerryItem
import com.cobblemon.mod.common.item.berry.PPRestoringBerryItem
import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.PotionItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.Block
import net.minecraft.block.ComposterBlock
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.effect.StatusEffectInstance
import net.minecraft.entity.effect.StatusEffects
import net.minecraft.item.*
@@ -32,6 +41,8 @@ import net.minecraft.registry.Registries
import net.minecraft.registry.Registry
import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.util.Identifier
import net.minecraft.world.World
@Suppress("unused")
object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<Item>>, Item>() {
@@ -220,8 +231,82 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField val PEAT_BLOCK = noSettingsItem("peat_block")
@JvmField val RAZOR_CLAW = noSettingsItem("razor_claw")
@JvmField val RAZOR_FANG = noSettingsItem("razor_fang")
// ToDo enable me when malicious armor goes in the game
//@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val AUSPICIOUS_ARMOR = heldItem("auspicious_armor")
@JvmField val MALICIOUS_ARMOR = heldItem("malicious_armor")
private val berries = mutableMapOf<Identifier, BerryItem>()
// Plants
@JvmField val ORAN_BERRY = berryItem("oran", HealingBerryItem(CobblemonBlocks.ORAN_BERRY) { CobblemonMechanics.berries.oranRestoreAmount })
@JvmField val CHERI_BERRY = berryItem("cheri", StatusCuringBerryItem(CobblemonBlocks.CHERI_BERRY, Statuses.PARALYSIS))
@JvmField val CHESTO_BERRY = berryItem("chesto", StatusCuringBerryItem(CobblemonBlocks.CHESTO_BERRY, Statuses.SLEEP))
@JvmField val PECHA_BERRY = berryItem("pecha", StatusCuringBerryItem(CobblemonBlocks.PECHA_BERRY, Statuses.POISON, Statuses.POISON_BADLY))
@JvmField val RAWST_BERRY = berryItem("rawst", StatusCuringBerryItem(CobblemonBlocks.RAWST_BERRY, Statuses.BURN))
@JvmField val ASPEAR_BERRY = berryItem("aspear", StatusCuringBerryItem(CobblemonBlocks.ASPEAR_BERRY, Statuses.FROZEN))
@JvmField val PERSIM_BERRY = berryItem("persim", VolatileCuringBerryItem(CobblemonBlocks.PERSIM_BERRY, "confusion"))
@JvmField val RAZZ_BERRY = berryItem("razz", CobblemonBlocks.RAZZ_BERRY)
@JvmField val BLUK_BERRY = berryItem("bluk", CobblemonBlocks.BLUK_BERRY)
@JvmField val NANAB_BERRY = berryItem("nanab", CobblemonBlocks.NANAB_BERRY)
@JvmField val WEPEAR_BERRY = berryItem("wepear", CobblemonBlocks.WEPEAR_BERRY)
@JvmField val PINAP_BERRY = berryItem("pinap", CobblemonBlocks.PINAP_BERRY)
@JvmField val OCCA_BERRY = berryItem("occa", CobblemonBlocks.OCCA_BERRY)
@JvmField val PASSHO_BERRY = berryItem("passho", CobblemonBlocks.PASSHO_BERRY)
@JvmField val WACAN_BERRY = berryItem("wacan", CobblemonBlocks.WACAN_BERRY)
@JvmField val RINDO_BERRY = berryItem("rindo", CobblemonBlocks.RINDO_BERRY)
@JvmField val YACHE_BERRY = berryItem("yache", CobblemonBlocks.YACHE_BERRY)
@JvmField val CHOPLE_BERRY = berryItem("chople", CobblemonBlocks.CHOPLE_BERRY)
@JvmField val KEBIA_BERRY = berryItem("kebia", CobblemonBlocks.KEBIA_BERRY)
@JvmField val SHUCA_BERRY = berryItem("shuca", CobblemonBlocks.SHUCA_BERRY)
@JvmField val COBA_BERRY = berryItem("coba", CobblemonBlocks.COBA_BERRY)
@JvmField val PAYAPA_BERRY = berryItem("payapa", CobblemonBlocks.PAYAPA_BERRY)
@JvmField val TANGA_BERRY = berryItem("tanga", CobblemonBlocks.TANGA_BERRY)
@JvmField val CHARTI_BERRY = berryItem("charti", CobblemonBlocks.CHARTI_BERRY)
@JvmField val KASIB_BERRY = berryItem("kasib", CobblemonBlocks.KASIB_BERRY)
@JvmField val HABAN_BERRY = berryItem("haban", CobblemonBlocks.HABAN_BERRY)
@JvmField val COLBUR_BERRY = berryItem("colbur", CobblemonBlocks.COLBUR_BERRY)
@JvmField val BABIRI_BERRY = berryItem("babiri", CobblemonBlocks.BABIRI_BERRY)
@JvmField val CHILAN_BERRY = berryItem("chilan", CobblemonBlocks.CHILAN_BERRY)
@JvmField val ROSELI_BERRY = berryItem("roseli", CobblemonBlocks.ROSELI_BERRY)
@JvmField val LEPPA_BERRY = berryItem("leppa", PPRestoringBerryItem(CobblemonBlocks.LEPPA_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
@JvmField val LUM_BERRY = berryItem("lum", StatusCuringBerryItem(CobblemonBlocks.LUM_BERRY))
@JvmField val FIGY_BERRY = berryItem("figy", PortionHealingBerryItem(CobblemonBlocks.FIGY_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val WIKI_BERRY = berryItem("wiki", PortionHealingBerryItem(CobblemonBlocks.WIKI_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val MAGO_BERRY = berryItem("mago", PortionHealingBerryItem(CobblemonBlocks.MAGO_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val AGUAV_BERRY = berryItem("aguav", PortionHealingBerryItem(CobblemonBlocks.AGUAV_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val IAPAPA_BERRY = berryItem("iapapa", PortionHealingBerryItem(CobblemonBlocks.IAPAPA_BERRY, true) { CobblemonMechanics.berries.portionHealRatio })
@JvmField val SITRUS_BERRY = berryItem("sitrus", HealingBerryItem(CobblemonBlocks.SITRUS_BERRY) { CobblemonMechanics.berries.sitrusHealAmount })
@JvmField val TOUGA_BERRY = berryItem("touga", CobblemonBlocks.TOUGA_BERRY)
@JvmField val CORNN_BERRY = berryItem("cornn", CobblemonBlocks.CORNN_BERRY)
@JvmField val MAGOST_BERRY = berryItem("magost", CobblemonBlocks.MAGOST_BERRY)
@JvmField val RABUTA_BERRY = berryItem("rabuta", CobblemonBlocks.RABUTA_BERRY)
@JvmField val NOMEL_BERRY = berryItem("nomel", CobblemonBlocks.NOMEL_BERRY)
@JvmField val ENIGMA_BERRY = berryItem("enigma", CobblemonBlocks.ENIGMA_BERRY)
@JvmField val POMEG_BERRY = berryItem("pomeg", FriendshipRaisingBerryItem(CobblemonBlocks.POMEG_BERRY, Stats.HP))
@JvmField val KELPSY_BERRY = berryItem("kelpsy", FriendshipRaisingBerryItem(CobblemonBlocks.KELPSY_BERRY, Stats.ATTACK))
@JvmField val QUALOT_BERRY = berryItem("qualot", FriendshipRaisingBerryItem(CobblemonBlocks.QUALOT_BERRY, Stats.DEFENCE))
@JvmField val HONDEW_BERRY = berryItem("hondew", FriendshipRaisingBerryItem(CobblemonBlocks.HONDEW_BERRY, Stats.SPECIAL_ATTACK))
@JvmField val GREPA_BERRY = berryItem("grepa", FriendshipRaisingBerryItem(CobblemonBlocks.GREPA_BERRY, Stats.SPECIAL_DEFENCE))
@JvmField val TAMATO_BERRY = berryItem("tamato", FriendshipRaisingBerryItem(CobblemonBlocks.TAMATO_BERRY, Stats.SPEED))
@JvmField val SPELON_BERRY = berryItem("spelon", CobblemonBlocks.SPELON_BERRY)
@JvmField val PAMTRE_BERRY = berryItem("pamtre", CobblemonBlocks.PAMTRE_BERRY)
@JvmField val WATMEL_BERRY = berryItem("watmel", CobblemonBlocks.WATMEL_BERRY)
@JvmField val DURIN_BERRY = berryItem("durin", CobblemonBlocks.DURIN_BERRY)
@JvmField val BELUE_BERRY = berryItem("belue", CobblemonBlocks.BELUE_BERRY)
@JvmField val KEE_BERRY = berryItem("kee", CobblemonBlocks.KEE_BERRY)
@JvmField val MARANGA_BERRY = berryItem("maranga", CobblemonBlocks.MARANGA_BERRY)
@JvmField val HOPO_BERRY = berryItem("hopo", PPRestoringBerryItem(CobblemonBlocks.HOPO_BERRY) { CobblemonMechanics.berries.ppRestoreAmount })
@JvmField val LIECHI_BERRY = berryItem("liechi", CobblemonBlocks.LIECHI_BERRY)
@JvmField val GANLON_BERRY = berryItem("ganlon", CobblemonBlocks.GANLON_BERRY)
@JvmField val SALAC_BERRY = berryItem("salac", CobblemonBlocks.SALAC_BERRY)
@JvmField val PETAYA_BERRY = berryItem("petaya", CobblemonBlocks.PETAYA_BERRY)
@JvmField val APICOT_BERRY = berryItem("apicot", CobblemonBlocks.APICOT_BERRY)
@JvmField val LANSAT_BERRY = berryItem("lansat", CobblemonBlocks.LANSAT_BERRY)
@JvmField val STARF_BERRY = berryItem("starf", CobblemonBlocks.STARF_BERRY)
@JvmField val MICLE_BERRY = berryItem("micle", CobblemonBlocks.MICLE_BERRY)
@JvmField val CUSTAP_BERRY = berryItem("custap", CobblemonBlocks.CUSTAP_BERRY)
@JvmField val JABOCA_BERRY = berryItem("jaboca", CobblemonBlocks.JABOCA_BERRY)
@JvmField val ROWAP_BERRY = berryItem("rowap", CobblemonBlocks.ROWAP_BERRY)
@JvmField val BERRY_JUICE = this.create("berry_juice", BerryJuiceItem())
// Medicine
@JvmField
@@ -255,14 +340,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val BRAISED_VIVICHOKE = create("braised_vivichoke", Item(Item.Settings().maxCount(1).food(FoodComponent.Builder().hunger(6).saturationModifier(0.6f).build())))
@JvmField
val VIVICHOKE_DIP = create("vivichoke_dip", StewItem(Item.Settings().maxCount(1)
val VIVICHOKE_DIP = create("vivichoke_dip", object : StewItem(Settings().maxCount(1)
.food(FoodComponent.Builder()
.hunger(10)
.saturationModifier(1.2F)
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 600, 0), 1F)
.build()
)
))
.statusEffect(StatusEffectInstance(StatusEffects.ABSORPTION, 900, 0), 1F)
.alwaysEdible()
.build())) {
override fun finishUsing(stack: ItemStack?, world: World?, user: LivingEntity?): ItemStack {
user?.clearStatusEffects()
return super.finishUsing(stack, world, user)
}
})
@JvmField
val ENERGY_ROOT = create("energy_root", EnergyRootItem(CobblemonBlocks.ENERGY_ROOT, Item.Settings().food(FoodComponent.Builder().hunger(1).snack().saturationModifier(0.2f).build())))
@JvmField
@@ -544,6 +633,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val DESTINY_KNOT = heldItem("destiny_knot")
@JvmField
val EVERSTONE = heldItem("everstone")
@JvmField
val POWER_ANKLET = heldItem("power_anklet")
@JvmField
val POWER_BAND = heldItem("power_band")
@JvmField
val POWER_BELT = heldItem("power_belt")
@JvmField
val POWER_BRACER = heldItem("power_bracer")
@JvmField
val POWER_LENS = heldItem("power_lens")
@JvmField
val POWER_WEIGHT = heldItem("power_weight")
// Mulch
@JvmField
@@ -573,6 +674,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Settings()))
fun berries() = this.berries.toMap()
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
private fun pokeBallItem(pokeBall: PokeBall): PokeBallItem {
@@ -593,4 +696,17 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
}
)
}
private fun berryItem(name: String, berryBlock: BerryBlock): BerryItem {
val finalName = "${name}_berry"
val item = this.create(finalName, BerryItem(berryBlock))
this.berries[cobblemonResource(finalName)] = item
return item
}
private fun berryItem(name: String, berryItem: BerryItem): BerryItem {
val finalName = "${name}_berry"
val item = this.create(finalName, berryItem)
this.berries[cobblemonResource(finalName)] = item
return item
}
}
@@ -11,6 +11,8 @@ package com.cobblemon.mod.common
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.data.DataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.mechanics.BerriesMechanic
import com.cobblemon.mod.common.mechanics.PotionsMechanic
import com.cobblemon.mod.common.mechanics.RemediesMechanic
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.cobblemonResource
@@ -30,11 +32,19 @@ object CobblemonMechanics : DataRegistry {
.create()
var remedies = RemediesMechanic()
var berries = BerriesMechanic()
var potions = PotionsMechanic()
override fun sync(player: ServerPlayerEntity) {}
override fun reload(manager: ResourceManager) {
manager.getResourceOrThrow(cobblemonResource("mechanics/remedies.json")).inputStream.use {
remedies = gson.fromJson(it.reader(), RemediesMechanic::class.java)
remedies = loadMechanic(manager, "remedies", RemediesMechanic::class.java)
berries = loadMechanic(manager, "berries", BerriesMechanic::class.java)
potions = loadMechanic(manager, "potions", PotionsMechanic::class.java)
}
private fun <T> loadMechanic(manager: ResourceManager, name: String, clazz: Class<T>): T {
manager.getResourceOrThrow(cobblemonResource("mechanics/$name.json")).inputStream.use {
return gson.fromJson(it.reader(), clazz)
}
}
}
@@ -13,14 +13,14 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.battle.*
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.data.DataRegistrySyncPacketHandler
import com.cobblemon.mod.common.client.net.data.UnlockReloadPacketHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
import com.cobblemon.mod.common.client.net.callback.partymove.OpenPartyMoveCallbackHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
@@ -56,6 +56,7 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPac
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
@@ -273,6 +274,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
// Effects
this.createClientBound(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler)
@@ -78,6 +78,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
@JvmField
val BERRY_HARVEST = this.create("berry.harvest")
@JvmField
val BERRY_EAT = this.create("berry.eat")
@JvmField
val MULCH_PLACE = this.create("mulch.place")
@JvmField
val MULCH_REMOVE = this.create("mulch.remove")
@JvmField
val MUSIC_DEEP_DARK = this.create("music.deep_dark")
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
}
@@ -47,13 +47,13 @@ object CobblemonCriteria {
val EVOLVE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_evolved"),
SimpleCountableCriterionCondition::class.java
EvolvePokemonCriterionCondition::class.java
)
)
val WIN_BATTLE = this.create(
SimpleCriterionTrigger(
BattleCountableCriterionTrigger(
cobblemonResource("battles_won"),
SimpleCountableCriterionCondition::class.java
BattleCountableCriterionCondition::class.java
)
)
@@ -71,6 +71,41 @@ object CobblemonCriteria {
)
)
val POKEMON_INTERACT = this.create(
SimpleCriterionTrigger(
cobblemonResource("pokemon_interact"),
PokemonInteractCriterion::class.java
)
)
val PARTY_CHECK = this.create(
SimpleCriterionTrigger(
cobblemonResource("party"),
PartyCheckCriterion::class.java
)
)
val LEVEL_UP = this.create(
SimpleCriterionTrigger(
cobblemonResource("level_up"),
LevelUpCriterionCondition::class.java
)
)
val PASTURE_USE = this.create(
SimpleCriterionTrigger(
cobblemonResource("pasture_use"),
PickStarterCriterionCondition::class.java
)
)
val TRADE_POKEMON = this.create(
SimpleCriterionTrigger(
cobblemonResource("trade_pokemon"),
TradePokemonCriterionCondition::class.java
)
)
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
}
@@ -0,0 +1,67 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.util.toJsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class BattleCountableCriterionTrigger(identifier: Identifier, criterionClass: Class<BattleCountableCriterionCondition>) : SimpleCriterionTrigger<BattleCountableContext, BattleCountableCriterionCondition>(identifier, criterionClass) {
}
class BattleCountableCriterionCondition(id: Identifier, predicate: LootContextPredicate) : CountableCriterionCondition<BattleCountableContext>(id, predicate) {
private var battleTypes = mutableListOf("any")
override fun fromJson(json: JsonObject) {
super.fromJson(json)
if(!json.get("battle_types").isJsonNull) {
battleTypes.clear()
json.get("battle_types").asJsonArray.asList().forEach() {
battleTypes.add(it.asString)
}
}
}
override fun toJson(json: JsonObject) {
super.toJson(json)
json.add("battle_types", battleTypes.toJsonArray())
}
override fun matches(player: ServerPlayerEntity, context: BattleCountableContext): Boolean {
var typeCheck = false
val advancementData = Cobblemon.playerData.get(player).advancementData
if (battleTypes.isEmpty() || battleTypes.contains("any")) {
typeCheck = true
}
if (battleTypes.contains("pvp")) {
typeCheck = context.battle.isPvP
context.times = advancementData.totalPvPBattleVictoryCount
}
if (battleTypes.contains("pvw")) {
typeCheck = context.battle.isPvW
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.contains("pvn")) {
typeCheck = context.battle.isPvN
context.times = advancementData.totalPvWBattleVictoryCount
}
if (battleTypes.size > 1) {
context.times = advancementData.totalBattleVictoryCount
}
return context.times >= count && typeCheck
}
}
open class BattleCountableContext(times : Int, var battle : PokemonBattle) : CountableContext(times)
@@ -34,6 +34,6 @@ class CaughtPokemonCriterionCondition(id: Identifier, predicate: LootContextPred
}
override fun matches(player: ServerPlayerEntity, context: CountablePokemonTypeContext): Boolean {
return super.matches(player, context) && context.type == type
return super.matches(player, context) && (context.type == type || type == "any")
}
}
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class EvolvePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : CountableCriterionCondition<EvolvePokemonContext>(id, entity) {
var species = "any"
var evolution = "any"
override fun toJson(json: JsonObject) {
super.toJson(json)
json.addProperty("species", species)
json.addProperty("evolution", evolution)
}
override fun fromJson(json: JsonObject) {
super.fromJson(json)
species = json.get("species")?.asString ?: "any"
evolution = json.get("evolution")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: EvolvePokemonContext): Boolean {
return context.times >= count && (context.species == species.asIdentifierDefaultingNamespace() || species == "any") &&
(context.evolution == evolution.asIdentifierDefaultingNamespace() || evolution == "any")
}
}
open class EvolvePokemonContext(val species : Identifier, val evolution : Identifier, times: Int) : CountableContext(times)
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class LevelUpCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<LevelUpContext>(id, entity) {
var level = 0
var evolved = true
override fun toJson(json: JsonObject) {
json.addProperty("level", level)
json.addProperty("has_evolved", evolved)
}
override fun fromJson(json: JsonObject) {
level = json.get("level")?.asInt ?: 0
evolved = json.get("has_evolved")?.asBoolean ?: true
}
override fun matches(player: ServerPlayerEntity, context: LevelUpContext): Boolean {
val preEvo = context.pokemon.preEvolution == null
val hasEvolution = !context.pokemon.evolutions.none()
val evolutionCheck = !(preEvo == hasEvolution)
return level == context.level && evolutionCheck == evolved
}
}
open class LevelUpContext(var level : Int, var pokemon : Pokemon)
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.api.storage.party.PlayerPartyStore
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.party
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class PartyCheckCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PartyCheckContext>(id, entity){
val party = mutableListOf<Identifier>()
override fun toJson(json: JsonObject) {
json.add("party", JsonArray(party.size).also {
party.forEach { pokemon -> it.add(pokemon.toString()) }
})
}
override fun fromJson(json: JsonObject) {
party.clear()
json.getAsJsonArray("party").forEach { element ->
party.add(element.asString.asIdentifierDefaultingNamespace())
}
}
override fun matches(player: ServerPlayerEntity, context: PartyCheckContext): Boolean {
val playerParty = player.party()
val matches = mutableListOf<Identifier>()
party.forEach {
if (it == "any".asIdentifierDefaultingNamespace()) {
matches.add(it)
}
}
val partyCount = playerParty.count()
if (matches.containsAll(party) && party.size == partyCount && matches.size == partyCount) return true
playerParty.iterator().forEach {
if (party.contains(it.species.resourceIdentifier)) {
matches.add(it.species.resourceIdentifier)
}
}
return matches.containsAll(party) && matches.size == partyCount
}
}
open class PartyCheckContext(val party : PlayerPartyStore)
@@ -0,0 +1,34 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
open class PokemonInteractContext(var type: Identifier, var item : Identifier)
class PokemonInteractCriterion(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<PokemonInteractContext>(id, entity) {
var type = "any"
var item = "any"
override fun toJson(json: JsonObject) {
json.addProperty("type", type)
json.addProperty("item", item)
}
override fun fromJson(json: JsonObject) {
type = json.get("type")?.asString ?: "any"
item = json.get("item")?.asString ?: "any"
}
override fun matches(player: ServerPlayerEntity, context: PokemonInteractContext): Boolean {
return (context.type == type.asIdentifierDefaultingNamespace() || type == "any") && (context.item == item.asIdentifierDefaultingNamespace() || item == "any")
}
}
@@ -0,0 +1,47 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
class TradePokemonCriterionCondition(id: Identifier, entity: LootContextPredicate) : SimpleCriterionCondition<TradePokemonContext>(id, entity) {
var traded = "any"
var received = "any"
var tradedHeldItem = "any"
var receivedHeldItem = "any"
override fun toJson(json: JsonObject) {
json.addProperty("traded", traded)
json.addProperty("received", received)
json.addProperty("traded_held_item", tradedHeldItem)
json.addProperty("received_held_item", receivedHeldItem)
}
override fun fromJson(json: JsonObject) {
traded = json.get("traded")?.asString ?: "any"
received = json.get("received")?.asString ?: "any"
tradedHeldItem = json.get("traded_held_item")?.asString ?: "minecraft:air"
receivedHeldItem = json.get("received_held_item")?.asString ?: "minecraft:air"
}
override fun matches(player: ServerPlayerEntity, context: TradePokemonContext): Boolean {
val heldItem1 = context.traded.heldItem().item.registryEntry.registryKey().value
val heldItem2 = context.received.heldItem().item.registryEntry.registryKey().value
return (context.traded.species.resourceIdentifier == traded.asIdentifierDefaultingNamespace() || traded == "any") &&
(context.received.species.resourceIdentifier == received.asIdentifierDefaultingNamespace() || received == "any") &&
(heldItem1 == tradedHeldItem.asIdentifierDefaultingNamespace() || heldItem1 == "minecraft:air".asIdentifierDefaultingNamespace()) &&
(heldItem2 == receivedHeldItem.asIdentifierDefaultingNamespace() || heldItem2 == "minecraft:air".asIdentifierDefaultingNamespace())
}
}
open class TradePokemonContext(val traded: Pokemon, val received: Pokemon)
@@ -34,22 +34,20 @@ import com.cobblemon.mod.common.battles.dispatch.GO
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.battles.interpreter.ContextManager
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.client.battle.BattleEndPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMessagePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleMusicPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DefeatEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.requirements.DefeatRequirement
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import net.minecraft.entity.Entity
import java.io.File
import java.util.UUID
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.text.Text
import java.io.File
import kotlin.io.path.Path
import kotlin.io.path.writeLines
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
/**
* Individual battle instance
@@ -62,7 +60,7 @@ open class PokemonBattle(
val side1: BattleSide,
val side2: BattleSide
) {
/** Whether or not logging will be silenced for this battle. */
/** Whether logging will be silenced for this battle. */
var mute = true
init {
@@ -211,11 +209,16 @@ open class PokemonBattle(
faintedPokemons.forEach { faintedPokemon ->
for (opponentPokemon in opponentNonFaintedPokemons) {
val facedFainted = opponentPokemon.facedOpponents.contains(faintedPokemon)
val defeatProgress = DefeatEvolutionProgress()
val pokemon = opponentPokemon.effectedPokemon
if (facedFainted && defeatProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target.matches(faintedPokemon.effectedPokemon) }) { defeatProgress }
progress.updateProgress(DefeatEvolutionProgress.Progress(progress.currentProgress().target, progress.currentProgress().amount + 1))
if (facedFainted) {
pokemon.evolutions.forEach { evolution ->
evolution.requirements.filterIsInstance<DefeatRequirement>().forEach { defeatRequirement ->
if (defeatRequirement.target.matches(faintedPokemon.effectedPokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DefeatEvolutionProgress && it.currentProgress().target == defeatRequirement.target }) { DefeatEvolutionProgress() }
progress.updateProgress(DefeatEvolutionProgress.Progress(defeatRequirement.target, progress.currentProgress().amount + 1))
}
}
}
}
val multiplier = when {
// ToDo when Exp. All is implement if enabled && !facedFainted return 2.0, probably should be a configurable value too, this will have priority over the Exp. Share
@@ -0,0 +1,109 @@
/*
* 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.berry
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.status.Status
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.adapters.CobblemonStatTypeAdapter
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import java.awt.Color
import net.minecraft.predicate.NumberRange
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.tag.TagKey
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.world.biome.Biome
/**
* The data registry for [Berry].
*
* @author Licious
* @since November 28th, 2022
*/
object Berries : JsonDataRegistry<Berry> {
override val id: Identifier = cobblemonResource("berries")
override val type: ResourceType = ResourceType.SERVER_DATA
override val observable = SimpleObservable<Berries>()
override val gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(MulchVariant::class.java, MulchVariantAdapter)
.registerTypeAdapter(NumberRange.FloatRange::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(Status::class.java, StatusAdapter)
.registerTypeAdapter(TypeToken.getParameterized(Collection::class.java, Box::class.java).type, BoxCollectionAdapter)
.registerTypeAdapter(Box::class.java, BoxAdapter)
.registerTypeAdapter(Vec3d::class.java, VerboseVec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(GrowthFactor::class.java, CobblemonGrowthFactorAdapter)
.registerTypeAdapter(IntRange::class.java, VerboseIntRangeAdapter)
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
.registerTypeAdapter(Stat::class.java, CobblemonStatTypeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(TagKey::class.java, Biome::class.java).type, TagKeyAdapter(RegistryKeys.BIOME))
.registerTypeAdapter(BerrySpawnCondition::class.java, CobblemonBerrySpawnConditionAdapter)
.create()
override val typeToken: TypeToken<Berry> = TypeToken.get(Berry::class.java)
override val resourcePath = "berries"
private val berries = hashMapOf<Identifier, Berry>()
val PECHA
get() = this.getByName("")
override fun reload(data: Map<Identifier, Berry>) {
this.berries.clear()
data.forEach { (identifier, berry) ->
try {
berry.identifier = identifier
berry.validate()
this.berries[identifier] = berry
} catch (e: Exception) {
Cobblemon.LOGGER.error("Skipped loading the {} berry", identifier, e)
}
}
Cobblemon.LOGGER.info("Loaded {} berries", this.berries.size)
this.observable.emit(this)
}
override fun sync(player: ServerPlayerEntity) {
BerryRegistrySyncPacket(this.all()).sendToPlayer(player)
}
fun all() = this.berries.values.toList()
/**
* Gets a berry if loaded.
*
* @param identifier The identifier of the berry.
* @return The [Berry] if loaded otherwise null.
*/
fun getByIdentifier(identifier: Identifier): Berry? = this.berries[identifier]
/**
* Gets a berry if loaded.
*
* @param name The path of the identifier of the berry under the [Cobblemon.MODID] namespace.
* @return The [Berry] if loaded otherwise null.
*/
fun getByName(name: String): Berry? = this.getByIdentifier(cobblemonResource(name))
}
@@ -0,0 +1,349 @@
/*
* 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.berry
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import com.cobblemon.mod.common.item.BerryItem
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.readBox
import com.cobblemon.mod.common.util.writeBox
import com.google.gson.annotations.SerializedName
import java.awt.Color
import java.util.EnumSet
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.client.model.ModelPart
import net.minecraft.entity.LivingEntity
import net.minecraft.network.PacketByteBuf
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Vec3d
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.World
import net.minecraft.world.biome.Biome
/**
* Represents the data behind a berry.
*
* @property identifier The [Identifier] of this berry.
* @property baseYield The [IntRange] possible for the berry tree before [bonusYield] is calculated.
* @property preferredBiomeTags The [TagKey]s of the berries preffered biomes. Determines spawning and yield
* @property growthTime The [IntRange] possible in minutes for how long the berry tree takes to grow.
* @property refreshRate The [IntRange] possible in minutes for how long the berry tree takes to regrow its berries once it has been harvested.
* @depreciated @property lifeCycles The [IntRange] possible for the berry to live for between harvests.
* @property favoriteMulches The types of mulches that benefit this berries yield
* @property growthFactors An array of [GrowthFactor]s that will affect this berry. The client is not aware of these.
* @property mutations A map of the partner berry as the key and the value as the resulting mutation with this berry.
* @property growthPoints A collection of [GrowthPoint]s for the berry flowers and fruit.
* @property sproutShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the sprouting stages.
* @property matureShapeBoxes A collection of [Box]es that make up the tree [VoxelShape] during the mature stages.
* @property flavors The [Flavor] values.
* @property tintIndexes Determines tints at specific indexes if any.
* @property flowerModelIdentifier The [Identifier] for the model of the berry in flower form.
* @property flowerTexture The [Identifier] for the texture of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitModelIdentifier The [Identifier] for the model of the berry in flower form. This is resolved into a [ModelPart] on the client.
* @property fruitTexture The [Identifier] for the texture of the berry in flower form.
*
* @throws IllegalArgumentException if the any yield range argument is not a positive range.
*/
class Berry(
identifier: Identifier,
val baseYield: IntRange,
val preferredBiomeTags: List<TagKey<Biome>>,
val growthTime: IntRange,
val refreshRate: IntRange,
//val lifeCycles: IntRange,
val favoriteMulches: EnumSet<MulchVariant>,
val growthFactors: Collection<GrowthFactor>,
val spawnConditions: List<BerrySpawnCondition>,
var growthPoints: Array<GrowthPoint>,
val randomizedGrowthPoints: Boolean = true,
val mutations: Map<Identifier, Identifier>,
@SerializedName("sproutShape")
private val sproutShapeBoxes: Collection<Box>,
@SerializedName("matureShape")
private val matureShapeBoxes: Collection<Box>,
private val flavors: Map<Flavor, Int>,
val tintIndexes: Map<Int, Color>,
@SerializedName("flowerModel")
val flowerModelIdentifier: Identifier,
val flowerTexture: Identifier,
@SerializedName("fruitModel")
val fruitModelIdentifier: Identifier,
val fruitTexture: Identifier,
val weight: Float
) {
@Transient
var identifier: Identifier = identifier
internal set
@Transient
private lateinit var shapedFlower: HashMap<Int, VoxelShape>
@Transient
private lateinit var shapedFruit: HashMap<Int, VoxelShape>
/**
* The [VoxelShape] of the tree during the sprouting stages.
*/
@Transient
lateinit var sproutShape: VoxelShape
private set
/**
* The [VoxelShape] of the tree during the mature stages.
*/
@Transient
lateinit var matureShape: VoxelShape
private set
init {
this.validate()
}
/**
* Finds the [BerryItem] that uses this berry for data.
*
* @return The [BerryItem] if existing.
*/
fun item(): BerryItem? = CobblemonItems.berries()[this.identifier]
/**
* Finds the [BerryBlock] that uses this berry for data.
*
* @return The [BerryBlock] if existing.
*/
fun block(): BerryBlock? = CobblemonBlocks.berries()[this.identifier]
/**
* Query the value of a certain flavor.
*
* @param flavor The [Flavor] being queried.
* @return The value if any or 0.
*/
fun flavor(flavor: Flavor): Int = this.flavors[flavor] ?: 0
fun dislikedBy(nature: Nature): Boolean {
val dislikedFlavor = nature.dislikedFlavor ?: return false
return flavor(dislikedFlavor) > 0
}
/**
* Calculates the yield for a berry tree being planted or replanted after a life cycle.
* Triggers [BerryYieldCalculationEvent].
*
* @param world The [World] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @param placer The [LivingEntity] tending to the tree, if any.
* @return The total berry stack count.
*/
fun calculateYield(world: World, state: BlockState, pos: BlockPos, placer: LivingEntity? = null): Int {
val base = this.baseYield.random()
val bonus = this.bonusYield(world, state, pos)
var yield = base + bonus.first
val event = BerryYieldCalculationEvent(this, world, state, pos, placer, yield, bonus.second)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (BerryBlock.getMulch(state) == MulchVariant.RICH) {
yield = yield.times(1.2).coerceAtMost(maxYield().toDouble()).toInt()
treeEntity.decrementMulchDuration(world, pos, state)
}
CobblemonEvents.BERRY_YIELD.post(event) { yield = it.yield }
return yield
//Just for testing the renderer
//return growthPoints.size
}
/**
* Calculates and returns the minimum possible yield by summing [baseYield] and [growthFactors] minimum values.
*
* @return The minimum possible yield.
*/
fun minYield() = this.baseYield.first + this.growthFactors.sumOf { it.minYield() }
/**
* Calculates and returns the maximum possible yield by summing [baseYield] and [growthFactors] max values.
*
* @return The maximum possible yield.
*/
fun maxYield() = this.baseYield.last + this.growthFactors.sumOf { it.maxYield() }
/**
* Checks if this berry can mutate with the given [partner].
* For the berry the mutations may produce use [mutationWith].
*
* @param partner The [Berry] being checked as a possible mutation partner.
* @return If a mutation is possible.
*/
fun canMutateWith(partner: Berry) = this.mutationWith(partner) != null
/**
* Query for the possible mutation with the given [partner].
*
* @param partner The [Berry] being mutated with.
* @return The resulting [Berry] if the mutation is possible.
*/
fun mutationWith(partner: Berry): Berry? {
val berryIdentifier = this.mutations[partner.identifier] ?: return null
return Berries.getByIdentifier(berryIdentifier)
}
/**
* Query for a possible partner to mutate into the given [resulting].
*
* @param resulting The [Berry] the mutation should result in.
* @return The [Berry] that mutates with this berry into the given [resulting] if possible.
*/
fun partnerForMutation(resulting: Berry): Berry? {
return this.mutations.firstNotNullOfOrNull { (partner, result) ->
if (result == resulting.identifier)
Berries.getByIdentifier(partner)
else
null
}
}
// A cheat since gson doesn't invoke init block
internal fun validate() {
if (this.baseYield.first < 0 || this.baseYield.last < 0) {
throw IllegalArgumentException("A berry base yield must be a positive range")
}
if (this.growthTime.first < 0 || this.growthTime.last < 0) {
throw IllegalArgumentException("The growth time must be a positive range")
}
if (this.refreshRate.first < 0 || this.refreshRate.last < 0) {
throw IllegalArgumentException("The refresh rate must be a positive range")
}
/*
if (this.lifeCycles.first < 0 || this.lifeCycles.last < 0) {
throw IllegalArgumentException("A berry life cycle must be a positive range")
}
*/
this.growthFactors.forEach { it.validateArguments() }
val maxYield = this.maxYield()
if (this.growthPoints.size < maxYield) {
throw IllegalArgumentException("Anchor points must have enough elements for the max possible yield of $maxYield you've provided ${this.growthPoints.size} points")
}
this.shapedFlower = hashMapOf()
this.shapedFruit = hashMapOf()
this.sproutShape = this.createAndUniteShapes(this.sproutShapeBoxes)
this.matureShape = this.createAndUniteShapes(this.matureShapeBoxes)
}
internal fun encode(buffer: PacketByteBuf) {
buffer.writeIdentifier(this.identifier)
buffer.writeInt(this.baseYield.first)
buffer.writeInt(this.baseYield.last)
buffer.writeEnumSet(favoriteMulches, MulchVariant::class.java)
//buffer.writeInt(this.lifeCycles.first)
//buffer.writeInt(this.lifeCycles.last)
buffer.writeInt(this.growthTime.first)
buffer.writeInt(this.growthTime.last)
buffer.writeInt(this.refreshRate.first)
buffer.writeInt(this.refreshRate.last)
buffer.writeCollection(this.growthPoints.toList()) { writer, value ->
writer.writeDouble(value.position.x)
writer.writeDouble(value.position.y)
writer.writeDouble(value.position.z)
writer.writeDouble(value.rotation.x)
writer.writeDouble(value.rotation.y)
writer.writeDouble(value.rotation.z)
}
buffer.writeBoolean(this.randomizedGrowthPoints)
buffer.writeMap(this.mutations, { writer, key -> writer.writeIdentifier(key) }, { writer, value -> writer.writeIdentifier(value) })
buffer.writeCollection(this.sproutShapeBoxes) { writer, value ->
writer.writeBox(value)
}
buffer.writeCollection(this.matureShapeBoxes) { writer, value ->
writer.writeBox(value)
}
buffer.writeMap(this.flavors, { writer, key -> writer.writeEnumConstant(key) }, { writer, value -> writer.writeInt(value) })
buffer.writeMap(this.tintIndexes, { writer, key -> writer.writeInt(key) }, { writer, value -> writer.writeInt(value.rgb) })
buffer.writeIdentifier(this.flowerModelIdentifier)
buffer.writeIdentifier(this.flowerTexture)
buffer.writeIdentifier(this.fruitModelIdentifier)
buffer.writeIdentifier(this.fruitTexture)
}
/**
* Calculates the bonus yield for the berry tree.
*
* @param world The [World] the tree is present in.
* @param state The [BlockState] of the tree.
* @param pos The [BlockPos] of the tree.
* @return The bonus yield, the growth factors that passed.
*/
private fun bonusYield(world: World, state: BlockState, pos: BlockPos): Pair<Int, Collection<GrowthFactor>> {
var bonus = 0
val passed = arrayListOf<GrowthFactor>()
val mulchVariant = BerryBlock.getMulch(state)
val hasBiomeMulch = mulchVariant in favoriteMulches
this.growthFactors.forEach { factor ->
if (factor.isValid(world, state, pos)) {
bonus += factor.yield()
passed += factor
}
else if (hasBiomeMulch) {
bonus += factor.yield()
}
}
return bonus to passed
}
private fun createAndUniteShapes(boxes: Collection<Box>): VoxelShape {
var shape: VoxelShape? = null
boxes.forEach { box ->
shape = if (shape == null) {
Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ)
} else {
VoxelShapes.union(shape, Block.createCuboidShape(box.minX, box.minY, box.minZ, box.maxX, box.maxY, box.maxZ))
}
}
return shape ?: VoxelShapes.fullCube()
}
companion object {
internal fun decode(buffer: PacketByteBuf): Berry {
val identifier = buffer.readIdentifier()
val baseYield = IntRange(buffer.readInt(), buffer.readInt())
val favMulchs = buffer.readEnumSet(MulchVariant::class.java)
//val lifeCycles = IntRange(buffer.readInt(), buffer.readInt())
val growthTime = IntRange(buffer.readInt(), buffer.readInt())
val refreshRate = IntRange(buffer.readInt(), buffer.readInt())
val growthPoints = buffer.readList { reader ->
GrowthPoint(Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()), Vec3d(reader.readDouble(), reader.readDouble(), reader.readDouble()))
}.toTypedArray()
val randomizedGrowthPoints = buffer.readBoolean()
val mutations = buffer.readMap({ reader -> reader.readIdentifier() }, { reader -> reader.readIdentifier() })
val sproutShapeBoxes = buffer.readList { it.readBox() }
val matureShapeBoxes = buffer.readList { it.readBox() }
val flavors = buffer.readMap({ reader -> reader.readEnumConstant(Flavor::class.java) }, { reader -> reader.readInt() })
val tintIndexes = buffer.readMap({ reader -> reader.readInt() }, { reader -> Color(reader.readInt()) })
val flowerModelIdentifier = buffer.readIdentifier()
val flowerTexture = buffer.readIdentifier()
val fruitModelIdentifier = buffer.readIdentifier()
val fruitTexture = buffer.readIdentifier()
return Berry(identifier, baseYield, emptyList(), growthTime, refreshRate, favMulchs, emptySet(), emptyList(), growthPoints, randomizedGrowthPoints, mutations, sproutShapeBoxes, matureShapeBoxes, flavors, tintIndexes, flowerModelIdentifier, flowerTexture, fruitModelIdentifier, fruitTexture, 0F)
}
}
}
@@ -0,0 +1,47 @@
/*
* 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.berry
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.world.feature.BerryGroveFeature
import com.google.common.cache.CacheBuilder
import com.google.common.cache.CacheLoader
import com.google.common.cache.LoadingCache
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.world.biome.Biome
object BerryHelper {
private val CACHE_LOADER = object : CacheLoader<RegistryEntry<Biome>, List<BerryBlock>>() {
override fun load(key: RegistryEntry<Biome>): List<BerryBlock> {
return naturalBerries.filter { berryBlock ->
val berry = berryBlock.berry()
berry?.spawnConditions?.any { it.canSpawn(berry, key) } ?: false
}
}
}
private val naturalBerries = CobblemonBlocks.berries().values.filter {
(it.berry()?.spawnConditions?.size ?: 0) > 0
}
private val validBerryCache: LoadingCache<RegistryEntry<Biome>, List<BerryBlock>> = CacheBuilder.newBuilder()
.maximumSize(4)
.build(CACHE_LOADER)
fun getBerriesForBiome(biome: RegistryEntry<Biome>): List<BerryBlock> {
return validBerryCache.get(biome)
}
fun getNaturallyGeneratingBerries(): List<BerryBlock> {
return naturalBerries
}
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
/**
* Represents the different flavors associated with berries and other Pokémon consumables.
* See the [Bulbapedia](https://bulbapedia.bulbagarden.net/wiki/Flavor) article for more information.
*
* @author Licious
* @since November 28th, 2022
*/
enum class Flavor {
SPICY,
DRY,
SWEET,
BITTER,
SOUR
}
@@ -0,0 +1,61 @@
/*
* 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.berry
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
/**
* Represents a condition that impacts extra berry yield.
*
* @author Licious
* @since December 2nd, 2022
*/
interface GrowthFactor {
/**
* Validates the arguments contained in this factor, this is invoked during loading.
*
* @throws IllegalArgumentException If an argument isn't valid.
*/
fun validateArguments()
/**
* Checks if this factor should yield a bonus.
*
* @param world The [WorldView] the berry tree is in.
* @param state The [BlockState] of the berry tree.
* @param pos The [BlockPos] of the berry tree.
* @return If the bonus should activate.
*/
fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean
/**
* Resolves the amount of bonus berries to grow if [isValid] was true.
*
* @return The amount of extra berries to grow.
*/
fun yield(): Int
/**
* Returns the minimum possible yield for this factor.
*
* @return The minimum possible yield for this factor.
*/
fun minYield(): Int
/**
* Returns the maximum possible yield for this factor.
*
* @return The maximum possible yield for this factor.
*/
fun maxYield(): Int
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry
import net.minecraft.util.math.Vec3d
/**
* Represents a position in a berry tree where a berry will flower then fruit.
*
* @property position The coordinates of the berry in the tree block.
* @property rotation The degree value for the rotation each axis.
*
* @author Licious
* @since December 19th, 2022
*/
data class GrowthPoint(
val position: Vec3d,
val rotation: Vec3d
)
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.adapter
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.adapters.CobblemonGrowthFactorAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [GrowthFactor]s.
* For the default implementation see [CobblemonGrowthFactorAdapter].
*
* @author Licious
* @since December 2nd, 2022
*/
interface GrowthFactorAdapter : JsonDeserializer<GrowthFactor>, JsonSerializer<GrowthFactor> {
/**
* Register a [GrowthFactor] to be used by this adapter.
*
* @param type The [KClass] of the [GrowthFactor].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out GrowthFactor>, identifier: Identifier)
}
@@ -0,0 +1,34 @@
/*
* 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.berry.adapter
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.api.berry.spawncondition.BerrySpawnCondition
import com.cobblemon.mod.common.util.adapters.CobblemonBerrySpawnConditionAdapter
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [GrowthFactor]s.
* For the default implementation see [CobblemonBerrySpawnConditionAdapter].
*
* @author Apion
* @since August 14, 2023
*/
interface SpawnConditionAdapter : JsonDeserializer<BerrySpawnCondition>, JsonSerializer<BerrySpawnCondition> {
/**
* Register a [BerrySpawnCondition] to be used by this adapter.
*
* @param type The [KClass] of the [BerrySpawnCondition].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out BerrySpawnCondition>, identifier: Identifier)
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
class AllBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
return true
}
override fun getGroveSize(random: Random): Int {
return random.nextBetween(minGroveSize, maxGroveSize)
}
companion object {
val ID = cobblemonResource("all_biome")
}
}
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
interface BerrySpawnCondition {
fun getGroveSize(random: Random): Int
fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean
}
@@ -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.api.berry.spawncondition
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.registry.entry.RegistryEntry
import net.minecraft.util.math.random.Random
import net.minecraft.world.biome.Biome
import kotlin.math.min
class PreferredBiomeCondition(val minGroveSize: Int, val maxGroveSize: Int) : BerrySpawnCondition{
override fun getGroveSize(random: Random): Int {
return random.nextBetween(minGroveSize, maxGroveSize)
}
override fun canSpawn(berry: Berry, biome: RegistryEntry<Biome>): Boolean {
val preferredBiomeTags = berry.preferredBiomeTags
return preferredBiomeTags.any {biome.isIn(it)}
}
companion object {
val ID = cobblemonResource("preferred_biome")
}
}
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.moves.Move
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
/**
* Used for opening a party select screen for players which routes to move selection when they choose a Pokémon.
@@ -30,6 +32,7 @@ object PartyMoveSelectCallbacks {
@JvmOverloads
fun create(
player: ServerPlayerEntity,
partyTitle: MutableText = lang("ui.party"),
pokemon: List<Pair<PartySelectPokemonDTO, List<MoveSelectDTO>>>,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (ServerPlayerEntity, pokemonIndex: Int, PartySelectPokemonDTO, moveIndex: Int, MoveSelectDTO) -> Unit
@@ -40,12 +43,13 @@ object PartyMoveSelectCallbacks {
handler = handler
)
callbacks[player.uuid] = callback
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, callback.pokemon))
player.sendPacket(OpenPartyMoveCallbackPacket(callback.uuid, partyTitle, callback.pokemon))
}
@JvmOverloads
fun createFromPokemon(
player: ServerPlayerEntity,
partyTitle: MutableText = lang("ui.party"),
pokemon: List<Pokemon>,
moves: (Pokemon) -> List<Move> = { it.moveSet.getMoves() },
canSelectPokemon: (Pokemon) -> Boolean = { true },
@@ -62,6 +66,7 @@ object PartyMoveSelectCallbacks {
create(
player = player,
partyTitle = partyTitle,
pokemon = pokemonList,
cancel = cancel,
handler = { _, pkIndex, _, moveIndex, _ ->
@@ -12,10 +12,10 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonPropertyExtractor
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
@@ -36,7 +36,7 @@ object PartySelectCallbacks {
@JvmOverloads
fun create(
player: ServerPlayerEntity,
title: Text = "".text(),
title: Text = lang("ui.party"),
pokemon: List<PartySelectPokemonDTO>,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (ServerPlayerEntity, index: Int) -> Unit
@@ -61,12 +61,14 @@ object PartySelectCallbacks {
@JvmOverloads
fun createBattleSelect(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
pokemon: List<BattlePokemon>,
canSelect: (BattlePokemon) -> Boolean,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (BattlePokemon) -> Unit
) = create(
player = player,
title = title,
cancel = cancel,
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk.effectedPokemon).also { it.enabled = canSelect(pk) } }
) { _, index -> handler(pokemon[index]) }
@@ -74,12 +76,14 @@ object PartySelectCallbacks {
@JvmOverloads
fun createFromPokemon(
player: ServerPlayerEntity,
title: Text = lang("ui.party"),
pokemon: List<Pokemon>,
canSelect: (Pokemon) -> Boolean,
cancel: (ServerPlayerEntity) -> Unit = {},
handler: (Pokemon) -> Unit
) = create(
player = player,
title = title,
cancel = cancel,
pokemon = pokemon.map { pk -> PartySelectPokemonDTO(pk).also { it.enabled = canSelect(pk) } }
) { _, index -> handler(pokemon[index]) }
@@ -118,7 +122,8 @@ class PartySelectCallback(
open class PartySelectPokemonDTO(
val pokemonProperties: PokemonProperties,
val heldItem: ItemStack = ItemStack.EMPTY,
var hpRatio: Float = 1F,
var currentHealth: Int,
var maxHealth: Int,
var enabled: Boolean
) {
@JvmOverloads
@@ -132,21 +137,24 @@ open class PartySelectPokemonDTO(
PokemonPropertyExtractor.STATUS
),
heldItem = pokemon.heldItemNoCopy(),
hpRatio = pokemon.currentHealth / pokemon.hp.toFloat(),
currentHealth = pokemon.currentHealth,
maxHealth = pokemon.hp,
enabled = enabled
)
constructor(buffer: PacketByteBuf): this(
pokemonProperties = PokemonProperties().loadFromNBT(buffer.readNbt() as NbtCompound),
heldItem = buffer.readItemStack(),
hpRatio = buffer.readFloat(),
currentHealth = buffer.readInt(),
maxHealth = buffer.readInt(),
enabled = buffer.readBoolean()
)
fun writeToBuffer(buffer: PacketByteBuf) {
buffer.writeNbt(pokemonProperties.saveToNBT())
buffer.writeItemStack(heldItem)
buffer.writeFloat(hpRatio)
buffer.writeInt(currentHealth)
buffer.writeInt(maxHealth)
buffer.writeBoolean(enabled)
}
}
@@ -46,6 +46,9 @@ interface JsonDataRegistry<T> : DataRegistry {
override fun reload(manager: ResourceManager) {
val data = hashMapOf<Identifier, T>()
manager.findResources(this.resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
}
resource.inputStream.use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = Identifier(identifier.namespace, File(identifier.path).nameWithoutExtension)
@@ -13,6 +13,10 @@ import com.cobblemon.mod.common.api.events.battles.BattleFledEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPostEvent
import com.cobblemon.mod.common.api.events.battles.BattleStartedPreEvent
import com.cobblemon.mod.common.api.events.battles.BattleVictoryEvent
import com.cobblemon.mod.common.api.events.berry.BerryHarvestEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.events.berry.BerryYieldCalculationEvent
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntityLoadEvent
import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveEvent
@@ -20,6 +24,8 @@ import com.cobblemon.mod.common.api.events.entity.PokemonEntitySaveToWorldEvent
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
import com.cobblemon.mod.common.api.events.item.LeftoversCreatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
import com.cobblemon.mod.common.api.events.pokemon.*
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionAcceptedEvent
@@ -59,6 +65,10 @@ object CobblemonEvents {
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@JvmField
val POKEMON_CATCH_RATE = EventObservable<PokemonCatchRateEvent>()
@JvmField
val POKE_BALL_CAPTURE_CALCULATED = EventObservable<PokeBallCaptureCalculatedEvent>()
@JvmField
val POKEMON_CAPTURED = EventObservable<PokemonCapturedEvent>()
// @JvmField
// val EGG_HATCH = EventObservable<HatchEggEvent>()
@@ -80,6 +90,9 @@ object CobblemonEvents {
@JvmField
val POKEMON_RECALLED = EventObservable<PokemonRecalledEvent>()
@JvmField
val TRADE_COMPLETED = EventObservable<TradeCompletedEvent>()
@JvmField
val LEVEL_UP_EVENT = EventObservable<LevelUpEvent>()
@@ -120,6 +133,15 @@ object CobblemonEvents {
@JvmField
val APRICORN_HARVESTED = EventObservable<ApricornHarvestEvent>()
// Berries
@JvmField
val BERRY_HARVEST = EventObservable<BerryHarvestEvent>()
@JvmField
val BERRY_MUTATION_OFFER = EventObservable<BerryMutationOfferEvent>()
@JvmField
val BERRY_MUTATION_RESULT = EventObservable<BerryMutationResultEvent>()
@JvmField
val BERRY_YIELD = EventObservable<BerryYieldCalculationEvent>()
@JvmField
val LEFTOVERS_CREATED = CancelableObservable<LeftoversCreatedEvent>()
@JvmField
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
/**
* The base of all Berry related events.
*
* @author Licious
* @since November 28th, 2022
*/
interface BerryEvent {
/**
* The [Berry] related to the event trigger
*/
val berry: Berry
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import net.minecraft.block.BlockState
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* An event fired when [BerryBlockEntity.harvest] is invoked.
*
* @property berry The [Berry] the tree is attached to.
* @property player The [ServerPlayerEntity] harvesting the tree.
* @property world The [World] the tree is in.
* @property pos The [BlockPos] of the tree.
* @property state The [BlockState] of the tree.
* @property blockEntity The backing [BerryBlockEntity]-
* @property drops A collection of [ItemStack]s produced by this harvest.
*
* @author Licious
* @since November 28th, 2022
*/
data class BerryHarvestEvent(
override val berry: Berry,
val player: ServerPlayerEntity,
val world: World,
val pos: BlockPos,
val state: BlockState,
val blockEntity: BerryBlockEntity,
val drops: MutableList<ItemStack>
) : BerryEvent
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property mutations The possible [Berry] mutations.
*
* @author Licious
* @since January 19th, 2022
*/
data class BerryMutationOfferEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val mutations: MutableSet<Berry>
) : BerryEvent
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.block.BerryBlock
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [BerryBlock.grow] is invoked and the tree passes to [BerryBlock.MATURE_AGE].
* This allows you to modify the possible mutations for the tree, for the event that decides which of these mutations is used see [BerryMutationResultEvent].
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property mutations The possible [Berry] mutations.
* @property pickedMutation The [Berry] chosen to mutate into, if null the mutation will not occur.
*
* @author Licious
* @since January 19th, 2022
*/
data class BerryMutationResultEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val mutations: Set<Berry>,
var pickedMutation: Berry?
) : BerryEvent
@@ -0,0 +1,58 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.berry
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import net.minecraft.block.BlockState
import net.minecraft.entity.LivingEntity
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
/**
* The event fired when [Berry.calculateYield] is invoked.
*
* @property world The [World] the berry tree is in.
* @property state The [BlockState] of the berry tree.
* @property pos The [BlockPos] of the berry tree.
* @property placer The [LivingEntity] triggering the calculation.
* @property yield The current yield of berries.
* @property passedGrowthFactors The [Berry.growthFactors] where [GrowthFactor.isValid] was true.
*
* @author Licious
* @since November 28th, 2022
*/
class BerryYieldCalculationEvent(
override val berry: Berry,
val world: World,
val state: BlockState,
val pos: BlockPos,
val placer: LivingEntity?,
yield: Int,
val passedGrowthFactors: Collection<GrowthFactor>
) : BerryEvent {
/**
* The amount of berries this tree will yield.
*
* @throws IllegalArgumentException If a new value exceeds [Berry.maxYield] or be lesser than 0.
*/
var yield: Int = yield
set(value) {
val max = this.berry.maxYield()
if (value > max) {
throw IllegalArgumentException("Cannot set the berry yield for ${this.berry.identifier} above $max")
}
if (value < 0) {
throw IllegalArgumentException("A berry tree cannot yield a negative amount of berries")
}
field = value
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.events.farming
import com.cobblemon.mod.common.api.apricorn.Apricorn
import net.minecraft.block.BlockState
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
@@ -18,6 +19,7 @@ import net.minecraft.util.math.BlockPos
*/
class ApricornHarvestEvent(
val player: ServerPlayerEntity,
val apricorn: Apricorn,
val world: ServerWorld,
val pos: BlockPos
) {
@@ -0,0 +1,35 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokeball
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.server.network.ServerPlayerEntity
/**
* Event fired when a Poké Ball has completed its capture calculation and is about to begin shaking
* or breaking free. The result of the capture can be changed by replacing [captureResult].
*
* @author Hiroku
* @since August 20th, 2023
*/
class PokeBallCaptureCalculatedEvent(
val thrower: LivingEntity,
val pokemonEntity: PokemonEntity,
val pokeBallEntity: EmptyPokeBallEntity,
var captureResult: CaptureContext
) {
fun ifPlayer(action: PokeBallCaptureCalculatedEvent.(player: ServerPlayerEntity) -> Unit) {
if (thrower is ServerPlayerEntity) {
action(this, thrower)
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokeball
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
/**
* Event fired when a capture calculator is accessing a Pokémon's catch rate. The [catchRate] property can
* be changed to adjust the rate.
*
* If you want to prevent a Pokémon from being caught, you should be handling the [PokeBallCaptureCalculatedEvent]
* event instead.
*
* @author Hiroku
* @since August 20th, 2023
*/
class PokemonCatchRateEvent(
val thrower: LivingEntity,
val pokeBallEntity: EmptyPokeBallEntity,
val pokemonEntity: PokemonEntity,
var catchRate: Float
)
@@ -8,10 +8,12 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.server.network.ServerPlayerEntity
data class PokemonCapturedEvent (
val pokemon: Pokemon,
val player: ServerPlayerEntity
val player: ServerPlayerEntity,
val pokeBallEntity: EmptyPokeBallEntity
)
@@ -0,0 +1,14 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeParticipant
class TradeCompletedEvent(val tradeParticipant1 : TradeParticipant, val tradeParticipant1Pokemon : Pokemon, val tradeParticipant2 : TradeParticipant, val tradeParticipant2Pokemon : Pokemon)
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay.Companion.PORTRAIT_DIAMETER
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Species
@@ -181,6 +182,10 @@ fun drawPortraitPokemon(
val model = PokemonModelRepository.getPoser(species.resourceIdentifier, aspects)
val texture = PokemonModelRepository.getTexture(species.resourceIdentifier, aspects, state)
val context = RenderContext()
PokemonModelRepository.variations[species.resourceIdentifier]?.getTexture(aspects, 0f).let { it -> context.put(RenderContext.TEXTURE, it) }
context.put(RenderContext.SCALE, species.getForm(aspects).baseScale)
val renderType = model.getLayer(texture)
RenderSystem.applyModelViewMatrix()
@@ -215,11 +220,12 @@ fun drawPortraitPokemon(
val packedLight = LightmapTextureManager.pack(11, 7)
model.withLayerContext(immediate, state, PokemonModelRepository.getLayers(species.resourceIdentifier, aspects)) {
model.render(matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
model.render(context, matrixStack, buffer, packedLight, OverlayTexture.DEFAULT_UV, 1F, 1F, 1F, 1F)
immediate.draw()
}
matrixStack.pop()
model.setDefault()
DiffuseLighting.enableGuiDepthLighting()
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.interaction
import net.minecraft.entity.Entity
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
/**
* An interaction that will affect an [Entity].
* These need to be triggered.
*
* @param T The type of the [Entity] this interaction will affect.
*/
interface EntityInteraction<T : Entity> {
/**
* Fired when a [ServerPlayerEntity] interacts with the target entity.
*
* @param player The [ServerPlayerEntity] interacting with the [entity].
* @param entity The [Entity] of type [T] being interacted.
* @param stack The [ItemStack] used in this interaction.
* @return true if the interaction was successful and no further interactions should be processed
*/
fun onInteraction(player: ServerPlayerEntity, entity: T, stack: ItemStack): Boolean
/**
* Decreases the stack size by a given amount.
* The stack size should be validated beforehand.
* If the [player] is in creative mode the decrement won't be performed.
*
* @param player The [ServerPlayerEntity] that caused the interaction, this is used to check for creative mode.
* @param stack The [ItemStack] being mutated.
* @param amount The amount to deduct from the stack, default is 1.
*/
fun consumeItem(player: ServerPlayerEntity, stack: ItemStack, amount: Int = 1) {
if (!player.isCreative) {
stack.decrement(amount)
}
}
}
@@ -0,0 +1,29 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.interaction
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import kotlin.reflect.KClass
import net.minecraft.util.Identifier
/**
* A type adapter for [EntityInteraction]s.
* For the default implementation with Pokémon entities see [CobblemonPokemonEntityInteractionTypeAdapter].
*
* @param T The type of [EntityInteraction].
*
* @author Licious
* @since November 30th, 2022
*/
interface EntityInteractionTypeAdapter<T : EntityInteraction<*>> : JsonDeserializer<T>, JsonSerializer<T> {
fun registerInteraction(identifier: Identifier, type: KClass<out T>)
}
@@ -6,23 +6,26 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.interactive
package com.cobblemon.mod.common.api.interaction
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.interactive.PokemonInteractiveItem.Ownership
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundEvent
/**
* An [InteractiveItem] targeting [PokemonEntity]s.
*
* @param accepted The [Ownership] variants for this interaction to fire [PokemonInteractiveItem.processInteraction].
* An [EntityInteraction] targeting [PokemonEntity]s.
* These need to be manually triggered if not implemented by an item.
*/
interface PokemonInteractiveItem : InteractiveItem<PokemonEntity> {
interface PokemonEntityInteraction : EntityInteraction<PokemonEntity> {
/**
* The accepted [Ownership] for the Pokémon entity in order for the interaction to fire.
*/
val accepted: Set<Ownership>
fun getSound(): SoundEvent? = CobblemonSounds.ITEM_USE
val sound: SoundEvent?
get() = CobblemonSounds.ITEM_USE
override fun onInteraction(player: ServerPlayerEntity, entity: PokemonEntity, stack: ItemStack): Boolean {
val pokemon = entity.pokemon
@@ -40,30 +43,15 @@ interface PokemonInteractiveItem : InteractiveItem<PokemonEntity> {
}
/**
* Fired after [InteractiveItem.onInteraction] the [Ownership] is checked if contained in [accepted].
* Fired after [EntityInteraction.onInteraction] the [Ownership] is checked if contained in [accepted].
*
* @param player The [ServerPlayerEntity] interacting with the [entity].
* @param entity The [PokemonEntity] being interacted with.
* @param stack The [ItemStack] used in this interaction. [ItemStack.getItem] will always be of the same type as this [InteractiveItem].
* @param stack The [ItemStack] used in this interaction.
* @return true if the interaction was successful and no further interactions should be processed.
*/
fun processInteraction(player: ServerPlayerEntity, entity: PokemonEntity, stack: ItemStack): Boolean
/**
* Decreases the stack size by a given amount.
* The stack size should be validated beforehand.
* If the [player] is in creative mode the decrement won't be performed.
*
* @param player The [ServerPlayerEntity] that caused the interaction, this is used to check for creative mode.
* @param stack The [ItemStack] being mutated.
* @param amount The amount to deduct from the stack, default is 1.
*/
fun consumeItem(player: ServerPlayerEntity, stack: ItemStack, amount: Int = 1) {
if (!player.isCreative) {
stack.decrement(amount)
}
}
/**
* Represents the ownership status of a Pokemon relative to a Player.
*
@@ -98,6 +98,11 @@ interface PokemonAndMoveSelectingItem {
fun canUseOnMove(move: Move): Boolean
fun interactWithSpecific(player: ServerPlayerEntity, stack: ItemStack, pokemon: Pokemon): TypedActionResult<ItemStack>? {
if (player.isSneaking) {
return TypedActionResult.pass(stack)
}
MoveSelectCallbacks.create(
player = player,
moves = pokemon.moveSet.toList(),
@@ -146,4 +151,4 @@ interface PokemonAndMoveSelectingItem {
return TypedActionResult.success(stack)
}
}
}
@@ -8,6 +8,8 @@
package com.cobblemon.mod.common.api.item
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.callback.PartySelectCallbacks
import com.cobblemon.mod.common.api.text.red
@@ -22,6 +24,7 @@ import com.cobblemon.mod.common.util.isHeld
import com.cobblemon.mod.common.util.isLookingAt
import com.cobblemon.mod.common.util.party
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.TypedActionResult
import net.minecraft.util.math.Box
@@ -59,15 +62,17 @@ interface PokemonSelectingItem {
return interactWithSpecificBattle(player, stack, battlePokemon)
}
} ?: run {
if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUuid == player.uuid) {
return applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
if (!player.isSneaking) {
return if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUuid == player.uuid) {
applyToPokemon(player, stack, pokemon) ?: TypedActionResult.pass(stack)
} else {
TypedActionResult.fail(stack)
}
} else {
return TypedActionResult.fail(stack)
interactGeneral(player, stack)
}
} else {
return interactGeneral(player, stack)
}
}
@@ -89,6 +94,7 @@ interface PokemonSelectingItem {
if (!player.isCreative) {
stack.decrement(1)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(battlePokemon.entity!!.pokemon.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
}
}
@@ -106,9 +112,14 @@ interface PokemonSelectingItem {
}
fun interactGeneral(player: ServerPlayerEntity, stack: ItemStack): TypedActionResult<ItemStack> {
val party = player.party().toList()
if (party.isEmpty()) {
return TypedActionResult.fail(stack)
}
PartySelectCallbacks.createFromPokemon(
player = player,
pokemon = player.party().toList(),
pokemon = party,
canSelect = ::canUseOnPokemon,
handler = { pk ->
if (stack.isHeld(player)) {
@@ -116,6 +127,7 @@ interface PokemonSelectingItem {
if (!player.isCreative) {
stack.decrement(1)
}
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(pk.species.resourceIdentifier, Registries.ITEM.getId(stack.item)))
}
}
)
@@ -18,6 +18,8 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
import net.minecraft.network.PacketByteBuf
import kotlin.math.min
class MoveSet : Iterable<Move> {
val observable = SimpleObservable<MoveSet>()
private var emit = true
@@ -63,6 +65,11 @@ class MoveSet : Iterable<Move> {
update()
}
fun partialHeal() {
getMoves().forEach { it.currentPp = min((it.currentPp + (it.maxPp / 2)), it.maxPp)}
update()
}
fun clear() {
doWithoutEmitting {
for (i in 0 until MOVE_COUNT){
@@ -8,38 +8,24 @@
package com.cobblemon.mod.common.api.mulch
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.item.MulchItem
import net.minecraft.util.StringIdentifiable
/**
* Represents the different types of Mulch implemented in the mod.
*
*/
enum class MulchVariant {
enum class MulchVariant(val duration: Int = -1) : StringIdentifiable {
COARSE,
GROWTH,
GROWTH(3),
HUMID,
LOAMY,
PEAT,
RICH,
RICH(3),
SANDY,
SURPRISE;
SURPRISE(3),
NONE;
/**
* Resolves the item version of the Mulch.
*
* @return The [MulchItem] equivalent.
*/
fun item(): MulchItem = when (this) {
COARSE -> CobblemonItems.COARSE_MULCH
GROWTH -> CobblemonItems.GROWTH_MULCH
HUMID -> CobblemonItems.HUMID_MULCH
LOAMY -> CobblemonItems.LOAMY_MULCH
PEAT -> CobblemonItems.PEAT_MULCH
RICH -> CobblemonItems.RICH_MULCH
SANDY -> CobblemonItems.SANDY_MULCH
SURPRISE -> CobblemonItems.SURPRISE_MULCH
override fun asString(): String {
return name.lowercase()
}
}
}
@@ -65,6 +65,7 @@ object CobblemonPermissions {
val TEST_PARTY_SLOT = this.create("${COMMAND_PREFIX}testpartyslot", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PARTY = this.create("${COMMAND_PREFIX}clearparty", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
val CLEAR_PC = this.create("${COMMAND_PREFIX}clearpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
fun all(): Iterable<Permission> = this.permissions
@@ -8,9 +8,12 @@
package com.cobblemon.mod.common.api.pokeball.catching.calculators
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokeball.PokemonCatchRateEvent
import com.cobblemon.mod.common.api.pokeball.catching.CaptureContext
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.LivingEntity
/**
@@ -24,7 +27,6 @@ import net.minecraft.entity.LivingEntity
* @since November 30, 2021
*/
interface CaptureCalculator {
/**
* The literal ID of this calculator.
* Used when registering the calculator to the registry in order to be used by the game rule.
@@ -37,10 +39,22 @@ interface CaptureCalculator {
* Processes a capture attempt with the given params.
*
* @param thrower The [LivingEntity] that threw the [PokeBall].
* @param pokeBall The [PokeBall] used.
* @param target The target [Pokemon] attempting to be captured.
* @return
* @param pokeBallEntity The [EmptyPokeBallEntity] used.
* @param target The target [PokemonEntity] attempting to be captured.
* @return a [CaptureContext] that is the result of the capture attempt.
*/
fun processCapture(thrower: LivingEntity, pokeBall: PokeBall, target: Pokemon) : CaptureContext
fun processCapture(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity) : CaptureContext
fun getCatchRate(thrower: LivingEntity, pokeBallEntity: EmptyPokeBallEntity, target: PokemonEntity, catchRate: Float): Float {
val event = PokemonCatchRateEvent(
thrower = thrower,
pokemonEntity = target,
pokeBallEntity = pokeBallEntity,
catchRate = catchRate
)
CobblemonEvents.POKEMON_CATCH_RATE.post(event)
return event.catchRate
}
}
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.api.pokemon
import com.cobblemon.mod.common.api.item.Flavor
import com.cobblemon.mod.common.api.berry.Flavor
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.util.cobblemonResource
@@ -166,6 +166,16 @@ object Natures {
return allNatures.find { nature -> nature.name == name }
}
/**
* Utility method to get a nature by string
* @return a nature type or null
*/
fun getNature(identifier: String): Nature? {
val nature = getNature(cobblemonResource(identifier))
if(nature != null) return nature
return getNature(Identifier(identifier))
}
/**
* Helper function for a random Nature
* @return a random nature type
@@ -15,16 +15,21 @@ import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.*
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.pokemon.status.PersistentStatusContainer
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isInt
import com.cobblemon.mod.common.util.splitMap
import com.google.gson.JsonArray
import com.google.gson.JsonObject
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.nbt.NbtElement
import net.minecraft.nbt.NbtList
@@ -86,6 +91,9 @@ open class PokemonProperties {
props.ability = parseStringOfRegistry(keyPairs, listOf("ability")) { Abilities.get(it)?.name }
props.status = parseStringOfRegistry(keyPairs, listOf("status")) { (Statuses.getStatus(it) ?: Statuses.getStatus(it.asIdentifierDefaultingNamespace()))?.showdownName }
props.nickname = parseText(keyPairs, listOf("nickname", "nick"))
props.teraType = parseStringOfRegistry(keyPairs, listOf("tera_type", "tera")) { ElementalTypes.get(it)?.name }
props.dmaxLevel = parseIntProperty(keyPairs, listOf("dmax_level", "dmax"))?.coerceIn(0, Cobblemon.config.maxDynamaxLevel)
props.gmaxFactor = parseBooleanProperty(keyPairs, listOf("gmax_factor", "gmax"))
val maybeIVs = IVs()
val maybeEVs = EVs()
@@ -147,9 +155,10 @@ open class PokemonProperties {
}
private fun parseSpeciesIdentifier(keyPairs: MutableList<Pair<String, String?>>): String? {
fun cleanSpeciesName(string: String) = string.lowercase().replace("[^a-z0-9:]".toRegex(), "")
val matched = getMatchedKeyPair(keyPairs, listOf("species"))
if (matched != null) {
val value = matched.second?.lowercase() ?: return null
val value = matched.second?.let(::cleanSpeciesName) ?: return null
return if (value.lowercase() == "random") {
"random"
} else {
@@ -168,7 +177,7 @@ open class PokemonProperties {
"random"
} else {
try {
val identifier = pair.first.asIdentifierDefaultingNamespace()
val identifier = cleanSpeciesName(pair.first).asIdentifierDefaultingNamespace()
val found = PokemonSpecies.getByIdentifier(identifier) ?: return@find false
if (found.resourceIdentifier.namespace == Cobblemon.MODID) found.resourceIdentifier.path else found.resourceIdentifier.toString()
} catch (e: InvalidIdentifierException) {
@@ -246,6 +255,9 @@ open class PokemonProperties {
var ability: String? = null
var aspects: Set<String> = emptySet()
var status: String? = null
var teraType: String? = null
var dmaxLevel: Int? = null
var gmaxFactor: Boolean? = null
var ivs: IVs? = null
var evs: EVs? = null
@@ -287,14 +299,17 @@ open class PokemonProperties {
customProperties.forEach { it.apply(pokemon) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemon.ivs[stat.key] = stat.value
pokemon.setIV(stat.key, stat.value)
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemon.evs[stat.key] = stat.value
pokemon.setEV(stat.key, stat.value)
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemon.teraType = type } }
dmaxLevel?.let { pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemon.gmaxFactor = it }
}
fun apply(pokemonEntity: PokemonEntity) {
@@ -322,14 +337,17 @@ open class PokemonProperties {
customProperties.forEach { it.apply(pokemonEntity) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemonEntity.pokemon.ivs[stat.key] = stat.value
pokemonEntity.pokemon.setIV(stat.key, stat.value)
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemonEntity.pokemon.evs[stat.key] = stat.value
pokemonEntity.pokemon.setEV(stat.key, stat.value)
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
}
fun matches(pokemon: Pokemon): Boolean {
@@ -363,6 +381,9 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemon.gmaxFactor }?.let { return false }
return customProperties.none { !it.matches(pokemon) }
}
@@ -395,6 +416,9 @@ open class PokemonProperties {
evs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
return customProperties.none { !it.matches(pokemonEntity) }
}
@@ -435,24 +459,33 @@ open class PokemonProperties {
if (stat.value != propertiesEVs[stat.key]) { return false }
}
}
teraType?.takeIf { it != properties.teraType }?.let { return false }
dmaxLevel?.takeIf { it != properties.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != properties.gmaxFactor }?.let { return false }
return true
}
fun create(): Pokemon {
val pokemon = Pokemon()
apply(pokemon)
// Force the setter to initialize it
pokemon.species = pokemon.species
// This should only be a thing once we have moveset control in properties until then a creation should require a moveset init.
/*
if (pokemon.moveSet.none { it != null }) {
pokemon.initializeMoveset()
}
*/
pokemon.initializeMoveset()
pokemon.initialize()
roll(pokemon)
return pokemon
}
// TEST YOUR LUCK!
fun roll(pokemon: Pokemon) {
val baseTypes = pokemon.species.types.toList()
if (this.shiny == null) pokemon.shiny = Cobblemon.config.shinyRate.checkRate()
if (this.gmaxFactor == null) pokemon.gmaxFactor = Cobblemon.config.gigantamaxFactorRate.checkRate()
if (this.teraType == null) pokemon.teraType =
if (Cobblemon.config.teraTypeRate.checkRate()) ElementalTypes.all().filter { !baseTypes.contains(it) }.random()
else baseTypes.random()
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
fun createEntity(world: World): PokemonEntity {
return PokemonEntity(world, create())
}
@@ -473,6 +506,9 @@ open class PokemonProperties {
status?.let { nbt.putString(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { nbt.put(DataKeys.POKEMON_IVS, it.saveToNBT(NbtCompound())) }
evs?.let { nbt.put(DataKeys.POKEMON_EVS, it.saveToNBT(NbtCompound())) }
teraType?.let { nbt.putString(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { nbt.putInt(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { nbt.putBoolean(DataKeys.POKEMON_GMAX_FACTOR, it) }
val custom = NbtList()
customProperties.map { NbtString.of(it.asString()) }.forEach { custom.add(it) }
nbt.put(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -494,6 +530,9 @@ open class PokemonProperties {
status = if (tag.contains(DataKeys.POKEMON_STATUS_NAME)) tag.getString(DataKeys.POKEMON_STATUS_NAME) else null
ivs = if (tag.contains(DataKeys.POKEMON_IVS)) ivs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_IVS)) as IVs? else null
evs = if (tag.contains(DataKeys.POKEMON_EVS)) evs?.loadFromNBT(tag.getCompound(DataKeys.POKEMON_EVS)) as EVs? else null
teraType = if (tag.contains(DataKeys.POKEMON_TERA_TYPE)) tag.getString(DataKeys.POKEMON_TERA_TYPE) else null
dmaxLevel = if (tag.contains(DataKeys.POKEMON_DMAX_LEVEL)) tag.getInt(DataKeys.POKEMON_DMAX_LEVEL) else null
gmaxFactor = if (tag.contains(DataKeys.POKEMON_GMAX_FACTOR)) tag.getBoolean(DataKeys.POKEMON_GMAX_FACTOR) else null
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, NbtElement.STRING_TYPE.toInt())
// This is kinda gross
custom.forEach { customProperties.addAll(parse(it.asString()).customProperties) }
@@ -517,6 +556,9 @@ open class PokemonProperties {
status?.let { json.addProperty(DataKeys.POKEMON_STATUS_NAME, it) }
ivs?.let { json.add(DataKeys.POKEMON_IVS, it.saveToJSON(JsonObject())) }
evs?.let { json.add(DataKeys.POKEMON_EVS, it.saveToJSON(JsonObject())) }
teraType?.let { json.addProperty(DataKeys.POKEMON_TERA_TYPE, it) }
dmaxLevel?.let { json.addProperty(DataKeys.POKEMON_DMAX_LEVEL, it) }
gmaxFactor?.let { json.addProperty(DataKeys.POKEMON_GMAX_FACTOR, it) }
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
json.add(DataKeys.POKEMON_PROPERTIES_CUSTOM, custom)
@@ -539,6 +581,9 @@ open class PokemonProperties {
status = json.get(DataKeys.POKEMON_STATUS_NAME)?.asString
ivs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_IVS))
evs?.loadFromJSON(json.getAsJsonObject(DataKeys.POKEMON_EVS))
teraType = json.get(DataKeys.POKEMON_TERA_TYPE)?.asString
dmaxLevel = json.get(DataKeys.POKEMON_DMAX_LEVEL)?.asInt
gmaxFactor = json.get(DataKeys.POKEMON_GMAX_FACTOR)?.asBoolean
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
// This is still kinda gross
custom?.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -565,6 +610,9 @@ open class PokemonProperties {
evs?.forEach{ stat ->
pieces.add("${stat.key}_ev=${stat.value}")
}
teraType?.let { pieces.add("tera_type=$it") }
dmaxLevel?.let { pieces.add("dmax_level=$it") }
gmaxFactor?.let { pieces.add("gmax_factor=$it") }
customProperties.forEach { pieces.add(it.asString()) }
return pieces.joinToString(separator)
}
@@ -34,7 +34,6 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
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.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -42,7 +41,8 @@ import com.cobblemon.mod.common.pokemon.SpeciesAdditions
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonPreEvolutionAdapter
import com.cobblemon.mod.common.pokemon.evolution.adapters.CobblemonRequirementAdapter
import com.cobblemon.mod.common.pokemon.helditem.CobblemonEmptyHeldItemManager
import com.cobblemon.mod.common.pokemon.evolution.adapters.NbtItemPredicateAdapter
import com.cobblemon.mod.common.pokemon.evolution.predicate.NbtItemPredicate
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.cobblemonResource
@@ -95,6 +95,7 @@ object PokemonSpecies : JsonDataRegistry<Species> {
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(EggGroup::class.java, EggGroupAdapter)
.registerTypeAdapter(StatusEffect::class.java, RegistryElementAdapter<StatusEffect> { Registries.STATUS_EFFECT })
.registerTypeAdapter(NbtItemPredicate::class.java, NbtItemPredicateAdapter)
.disableHtmlEscaping()
.enableComplexMapKeySerialization()
.create()
@@ -238,11 +239,14 @@ object PokemonSpecies : JsonDataRegistry<Species> {
val heightm = form?.height ?: species.height
val weightkg = form?.weight ?: species.weight
// This is ugly, but we already have it hardcoded in the mod anyway
val maxHp = if (species.showdownId() == "shedinja") 1 else null
val maxHP = if (species.showdownId() == "shedinja") 1 else null
val canGigantamax: String? = if (form?.gigantamaxMove != null) form.gigantamaxMove.name else null
val cannotDynamax = form?.dynamaxBlocked ?: species.dynamaxBlocked
// ToDo battleOnly
// ToDo changesFrom
val requiredMove = form?.requiredMove
val requiredItem = form?.requiredItem
val requiredItems = form?.requiredItems
}
private fun createShowdownName(species: Species): String {
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.stats
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
/**
@@ -53,9 +54,24 @@ interface EvCalculator {
object Generation8EvCalculator : EvCalculator {
private val powerItems = mapOf(
Stats.SPEED to CobblemonItemTags.POWER_ANKLET,
Stats.SPECIAL_DEFENCE to CobblemonItemTags.POWER_BAND,
Stats.DEFENCE to CobblemonItemTags.POWER_BELT,
Stats.ATTACK to CobblemonItemTags.POWER_BRACER,
Stats.SPECIAL_ATTACK to CobblemonItemTags.POWER_LENS,
Stats.HP to CobblemonItemTags.POWER_WEIGHT
)
override fun calculate(battlePokemon: BattlePokemon, opponentPokemon: BattlePokemon): Map<Stat, Int> {
// ToDo Once held items and EV related items are implemented update this
return opponentPokemon.originalPokemon.form.evYield
val heldItem = battlePokemon.effectedPokemon.heldItemNoCopy()
val evYield = mutableMapOf<Stat, Int>()
for ((stat, value) in opponentPokemon.originalPokemon.form.evYield) {
val boost = if (!heldItem.isEmpty && heldItem.isIn(powerItems[stat])) 8 else 0
evYield[stat] = evYield.getOrDefault(stat, 0) + value + boost
}
return evYield
}
}
@@ -14,9 +14,7 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.weightedSelection
import kotlin.random.Random
/**
* A [SpawnAction] that will spawn a single [PokemonEntity].
@@ -35,10 +33,6 @@ class PokemonSpawnAction(
if (props.level == null) {
props.level = detail.getDerivedLevelRange().random()
}
if (props.shiny == null) {
// If the config value is at least 1, then do 1/x and use that as the shiny chance
props.shiny = Cobblemon.config.shinyRate.takeIf { it >= 1 }?.let { Random.Default.nextFloat() < 1 / it }
}
val heldItems = detail.heldItems?.takeIf { it.isNotEmpty() }?.toMutableList() ?: mutableListOf()
val heldItem = if (heldItems.isNotEmpty()) {
val until100 = 1 - heldItems.sumOf { it.percentage / 100 }
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import com.google.gson.annotations.SerializedName
import kotlin.math.ceil
@@ -48,6 +49,8 @@ class PokemonSpawnDetail : SpawnDetail() {
override fun getName(): MutableText {
displayName?.let { return it.asTranslated() }
val speciesString = pokemon.species
if (speciesString != null) {
if (speciesString.lowercase() == "random") {
@@ -1,20 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
open class GameRuleInfluence : SpawningInfluence {
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
return detail !is PokemonSpawnDetail || ctx.world.gameRules.getBoolean(DO_POKEMON_SPAWNING)
}
}
@@ -30,6 +30,7 @@ import net.minecraft.util.math.Vec3d
import net.minecraft.world.World
import net.minecraft.world.chunk.Chunk
import net.minecraft.world.chunk.ChunkStatus
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules.DO_POKEMON_SPAWNING
/**
* A type of [TickingSpawner] that operates within some area. When this spawner type
@@ -60,6 +61,9 @@ abstract class AreaSpawner(
override fun run(cause: SpawnCause): Pair<SpawningContext, SpawnDetail>? {
val area = getArea(cause)
if (area?.world?.gameRules?.getBoolean(DO_POKEMON_SPAWNING) == false) {
return null
}
val constrainedArea = if (area != null) constrainArea(area) else null
if (constrainedArea != null) {
val numberNearby = constrainedArea.world.getEntitiesByClass(
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.GameRuleInfluence
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import net.minecraft.server.network.ServerPlayerEntity
@@ -26,7 +25,7 @@ import net.minecraft.server.network.ServerPlayerEntity
*/
object PlayerSpawnerFactory {
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) }, { GameRuleInfluence() })
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
val influences = influenceBuilders.mapNotNull { it(player) }
@@ -34,4 +33,4 @@ object PlayerSpawnerFactory {
it.influences.addAll(influences)
}
}
}
}
@@ -121,8 +121,12 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
evs[Stats.SPECIAL_DEFENCE] = nbt.getInt("EVSpecialDefense")
evs[Stats.SPEED] = nbt.getInt("EVSpeed")
result.evs = evs
result.ivs = ivs
ivs.forEach { stat ->
result.setIV(stat.key, stat.value)
}
evs.forEach { stat ->
result.setEV(stat.key, stat.value)
}
for (move in nbt.getList("Moveset", 10)) {
val compound = move as NbtCompound
@@ -9,6 +9,8 @@
package com.cobblemon.mod.common.api.storage.party
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -18,13 +20,13 @@ import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.isPokemonEntity
import com.cobblemon.mod.common.util.lang
import java.util.UUID
import kotlin.math.round
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
import java.util.*
import kotlin.math.ceil
import kotlin.math.round
import kotlin.random.Random
/**
* A [PartyStore] used for a single player. This uses the player's UUID as the store's UUID, and is declared as its own
@@ -52,6 +54,7 @@ open class PlayerPartyStore(
override fun add(pokemon: Pokemon): Boolean {
return if (super.add(pokemon)) {
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
true
} else {
val player = playerUUID.getPlayer()
@@ -151,4 +154,31 @@ open class PlayerPartyStore(
}
return true
}
override fun swap(position1: PartyPosition, position2: PartyPosition) {
super.swap(position1, position2)
//Make it so we can check what's in the Player's party
val pokemon1 = get(position1)
val pokemon2 = get(position2)
if (pokemon1 != null && pokemon2 != null) {
val player = pokemon1.getOwnerPlayer()
if (player != null) {
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
}
} else if (pokemon1 != null || pokemon2 != null) {
var player = pokemon1?.getOwnerPlayer()
if (player != null) {
CobblemonCriteria.PARTY_CHECK.trigger(player, PartyCheckContext(this))
} else {
player = pokemon2!!.getOwnerPlayer()
CobblemonCriteria.PARTY_CHECK.trigger(player!!, PartyCheckContext(this))
}
}
}
override fun set(position: PartyPosition, pokemon: Pokemon) {
super.set(position, pokemon)
pokemon.getOwnerPlayer()?.let { CobblemonCriteria.PARTY_CHECK.trigger(it, PartyCheckContext(this)) }
}
}
@@ -24,8 +24,16 @@ class PlayerAdvancementData {
private set
var totalBattleVictoryCount: Int = 0
private set
var totalPvPBattleVictoryCount: Int = 0
private set
var totalPvWBattleVictoryCount: Int = 0
private set
var totalPvNBattleVictoryCount: Int = 0
private set
var totalShinyCaptureCount: Int = 0
private set
var totalTradedCount: Int = 0
private set
private var totalTypeCaptureCounts = mutableMapOf<String, Int>()
private var totalDefeatedCounts = mutableMapOf<Identifier, Int>()
@@ -45,13 +53,29 @@ class PlayerAdvancementData {
}
fun updateTotalBattleVictoryCount() {
totalEvolvedCount++
totalBattleVictoryCount++
}
fun updateTotalPvPBattleVictoryCount() {
totalPvPBattleVictoryCount++
}
fun updateTotalPvWBattleVictoryCount() {
totalPvWBattleVictoryCount++
}
fun updateTotalPvNBattleVictoryCount() {
totalPvNBattleVictoryCount++
}
fun updateTotalShinyCaptureCount() {
totalShinyCaptureCount++
}
fun updateTotalTradedCount() {
totalTradedCount++
}
fun getTotalTypeCaptureCount(type: ElementalType): Int {
if (!totalTypeCaptureCounts.containsKey(key = type.name)) {
totalTypeCaptureCounts[type.name] = 0
@@ -11,8 +11,10 @@ package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.UUID
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
data class PlayerData(
val uuid: UUID,
@@ -20,7 +22,8 @@ data class PlayerData(
var starterLocked: Boolean,
var starterSelected: Boolean,
var starterUUID: UUID?,
val extraData: MutableMap<String, PlayerDataExtension>
var keyItems: MutableSet<Identifier>,
val extraData: MutableMap<String, PlayerDataExtension>,
) {
var advancementData: PlayerAdvancementData = PlayerAdvancementData()
@@ -36,6 +39,7 @@ data class PlayerData(
starterLocked = !Cobblemon.starterConfig.allowStarterOnJoin,
starterSelected = false,
starterUUID = null,
keyItems = mutableSetOf(),
extraData = mutableMapOf()
)
}
@@ -8,13 +8,10 @@
package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerAdvancementData
import com.cobblemon.mod.common.api.storage.player.PlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.fromJson
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.removeIf
import com.google.gson.GsonBuilder
import java.io.BufferedReader
import java.io.FileReader
@@ -24,6 +21,8 @@ import java.util.UUID
import net.minecraft.server.MinecraftServer
import net.minecraft.util.Identifier
import net.minecraft.util.WorldSavePath
import kotlin.reflect.KMutableProperty
import kotlin.reflect.full.memberProperties
class JsonPlayerData: PlayerDataStoreAdapter {
@@ -57,9 +56,11 @@ class JsonPlayerData: PlayerDataStoreAdapter {
playerFile.parentFile.mkdirs()
return if (playerFile.exists()) {
gson.fromJson<PlayerData>(BufferedReader(FileReader(playerFile))).also {
// Resolves old data from pre 1.0, don't even ask man. - Hiroku
if (it.advancementData == null) {
it.advancementData = PlayerAdvancementData()
// Resolves old data that's missing new properties
val newProps = it::class.memberProperties.filterIsInstance<KMutableProperty<*>>().filter { member -> member.getter.call(it) == null }
if (newProps.isNotEmpty()) {
val defaultData = PlayerData.defaultData(uuid)
newProps.forEach { member -> member.setter.call(it, member.getter.call(defaultData)) }
}
}
} else {
@@ -119,6 +119,10 @@ object CobblemonBiomeTags {
val IS_VOlCANIC = create("is_volcanic")
@JvmField
val IS_WINTER = create("is_winter")
@JvmField
val IS_SPARSE = create("is_sparse")
@JvmField
val IS_DENSE = create("is_dense")
// Has Feature tags
@JvmField
@@ -26,6 +26,9 @@ object CobblemonBlockTags {
@JvmField val APRICORN_LOGS = createTag("apricorn_logs")
@JvmField val APRICORN_SAPLINGS = createTag("apricorn_saplings")
@JvmField val APRICORNS = createTag("apricorns")
@JvmField val BERRY_WILD_SOIL = createTag("berry_wild_soil")
@JvmField val BERRY_SOIL = createTag("berry_soil")
@JvmField val BERRY_REPLACEABLE = createTag("berry_replaceable")
@JvmField val CROPS = createTag("crops")
@JvmField val DRIPSTONE_GROWABLE = createTag("dripstone_growable")
@JvmField val DRIPSTONE_REPLACEABLES = createTag("dripstone_replaceables")
@@ -35,7 +38,8 @@ object CobblemonBlockTags {
@JvmField val ROOTS_SPREADABLE = createTag("roots_spreadable")
@JvmField val SMALL_FLOWERS = createTag("small_flowers")
@JvmField val SEES_SKY = createTag("sees_sky")
@JvmField val SNOW_BLOCK = createTag("snow_block")
@JvmField val ROOTS = createTag("roots")
private fun createTag(name: String) = TagKey.of(RegistryKeys.BLOCK, cobblemonResource(name))
}
}
@@ -42,6 +42,18 @@ object CobblemonItemTags {
@JvmField
val EVERSTONE = create("held/everstone")
@JvmField
val POWER_ANKLET = create("held/power_anklet")
@JvmField
val POWER_BAND = create("held/power_band")
@JvmField
val POWER_BELT = create("held/power_belt")
@JvmField
val POWER_BRACER = create("held/power_bracer")
@JvmField
val POWER_LENS = create("held/power_lens")
@JvmField
val POWER_WEIGHT = create("held/power_weight")
@JvmField
val EVOLUTION_STONES = create("evolution_stones")
@JvmField
val EVOLUTION_ITEMS = create("evolution_items")
@@ -169,4 +169,6 @@ object ElementalTypes {
}
fun count() = allTypes.size
fun all() = this.allTypes.toList()
}
@@ -21,24 +21,27 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.party
import java.util.Optional
import java.util.UUID
import net.minecraft.entity.Entity
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import java.util.*
import kotlin.collections.HashMap
object BattleBuilder {
@JvmOverloads
fun pvp1v1(
player1: ServerPlayerEntity,
player2: ServerPlayerEntity,
leadingPokemonPlayer1: UUID? = null,
leadingPokemonPlayer2: UUID? = null,
battleFormat: BattleFormat = BattleFormat.GEN_9_SINGLES,
cloneParties: Boolean = false,
healFirst: Boolean = false,
partyAccessor: (ServerPlayerEntity) -> PartyStore = { it.party() }
): BattleStartResult {
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, checkHealth = !healFirst)
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, checkHealth = !healFirst)
val team1 = partyAccessor(player1).toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemonPlayer1)
val team2 = partyAccessor(player2).toBattleTeam(clone = cloneParties, checkHealth = !healFirst, leadingPokemonPlayer2)
val player1Actor = PlayerBattleActor(player1.uuid, team1)
val player2Actor = PlayerBattleActor(player2.uuid, team2)
@@ -89,6 +92,7 @@ object BattleBuilder {
* @param fleeDistance How far away the player must get to flee the Pokémon. If the value is -1, it cannot be fled.
* @param party The party of the player to use for the battle. This does not need to be their actual party. Defaults to it though.
*/
@JvmOverloads
fun pve(
player: ServerPlayerEntity,
pokemonEntity: PokemonEntity,
@@ -28,7 +28,7 @@ data class BattleFormat(
companion object {
val GEN_9_SINGLES = BattleFormat(
battleType = BattleTypes.SINGLES,
ruleSet = setOf(BattleRules.OBTAINABLE)
ruleSet = setOf(BattleRules.OBTAINABLE, BattleRules.PAST, BattleRules.UNOBTAINABLE)
)
val GEN_9_DOUBLES = BattleFormat(
@@ -16,9 +16,9 @@ import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.cobblemon.mod.common.net.messages.client.battle.BattleChallengeExpiredPacket
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.battles.runner.ShowdownService
import com.google.gson.GsonBuilder
import java.time.Instant
import java.util.Optional
@@ -31,13 +31,17 @@ object BattleRegistry {
class BattleChallenge(
val challengeId: UUID,
val challengedPlayerUUID: UUID,
val selectedPokemonId: UUID,
var expiryTimeSeconds: Int = 60
) {
val challengedTime = Instant.now()
fun isExpired() = Instant.now().isAfter(challengedTime.plusSeconds(expiryTimeSeconds.toLong()))
}
val gson = GsonBuilder().disableHtmlEscaping().create()
val gson = GsonBuilder()
.disableHtmlEscaping()
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
.create()
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
// Challenger to challenge
val pvpChallenges = mutableMapOf<UUID, BattleChallenge>()
@@ -104,7 +108,7 @@ object BattleRegistry {
}|"
)
// Nature
val battleNature = pk.mintedNature ?: pk.nature
val battleNature = pk.effectiveNature
packedTeamBuilder.append("${battleNature.name.path}|")
// EVs
val evsInOrder = Stats.PERMANENT.map { pk.evs.getOrDefault(it) }.joinToString(separator = ",")
@@ -129,11 +133,12 @@ object BattleRegistry {
// Hidden Power Type
packedTeamBuilder.append(",")
// Gigantamax
packedTeamBuilder.append(",")
packedTeamBuilder.append("${if (pk.gmaxFactor) "G" else ""},")
// DynamaxLevel
packedTeamBuilder.append(",")
// 0 - 9, empty == 10
packedTeamBuilder.append("${if (pk.dmaxLevel < 10) pk.dmaxLevel else ""},")
// Teratype
packedTeamBuilder.append(",")
packedTeamBuilder.append("${pokemon.effectedPokemon.teraType.name},")
team.add(packedTeamBuilder.toString())
}
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.battles
object BattleRules {
const val OBTAINABLE = "Obtainable"
const val PAST = "+Past"
const val UNOBTAINABLE = "+Unobtainable"
const val TEAM_PREVIEW = "Team Preview"
const val ENDLESS_BATTLE_CLAUSE = "Endless Battle Clause"
const val CANCEL_MOD = "Cancel Mod"
@@ -19,6 +19,7 @@ class InBattleMove {
var maxpp: Int = 100
var target: MoveTarget = MoveTarget.self
var disabled: Boolean = false
var gimmickMove: InBattleGimmickMove? = null
companion object {
fun loadFromBuffer(buffer: PacketByteBuf): InBattleMove {
@@ -44,4 +45,27 @@ class InBattleMove {
buffer.writeEnumConstant(target)
buffer.writeBoolean(disabled)
}
}
// Defined in sim/battle-actions.ts canZMove and getMaxMove
class InBattleGimmickMove {
lateinit var move: String
var target: MoveTarget = MoveTarget.self
var disabled: Boolean = false
companion object {
fun loadFromBuffer(buffer: PacketByteBuf): InBattleGimmickMove {
return InBattleGimmickMove().apply {
move = buffer.readString()
target = buffer.readEnumConstant(MoveTarget::class.java)
disabled = buffer.readBoolean()
}
}
}
fun saveToBuffer(buffer: PacketByteBuf) {
buffer.writeString(move)
buffer.writeEnumConstant(target)
buffer.writeBoolean(disabled)
}
}
@@ -8,14 +8,22 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.net.IntSize
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.hasKeyItem
import com.cobblemon.mod.common.util.readSizedInt
import com.cobblemon.mod.common.util.writeSizedInt
import com.google.gson.*
import com.google.gson.reflect.TypeToken
import java.lang.Integer.max
import java.util.UUID
import net.minecraft.network.PacketByteBuf
import java.lang.reflect.Type
class ShowdownActionRequest(
var wait: Boolean = false,
var active: MutableList<ShowdownMoveset>? = null,
@@ -63,6 +71,22 @@ class ShowdownActionRequest(
}
return this
}
fun sanitize(battle: PokemonBattle, battleActor: BattleActor) {
val player = battle.players.find { it.uuid == battleActor.uuid } ?: return
this.active?.forEach { moveset ->
moveset.getGimmicks().forEach { gimmick ->
// TODO: use the Identifiers of actual items
val triggerItem = when(gimmick) {
ShowdownMoveset.Gimmick.MEGA_EVOLUTION -> cobblemonResource("key_stone")
ShowdownMoveset.Gimmick.DYNAMAX -> cobblemonResource("dynamax_band")
ShowdownMoveset.Gimmick.TERASTALLIZATION -> cobblemonResource("tera_orb")
else -> cobblemonResource("z_ring")
}
if (!player.hasKeyItem(triggerItem)) moveset.blockGimmick(gimmick)
}
}
}
}
enum class ShowdownActionResponseType(val loader: (PacketByteBuf) -> ShowdownActionResponse) {
@@ -91,18 +115,20 @@ abstract class ShowdownActionResponse(val type: ShowdownActionResponseType) {
abstract fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?): String
}
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null, var gimmickID: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
if (forceSwitch || showdownMoveSet == null) {
return false
}
val move = showdownMoveSet.moves.find { it.id == moveName } ?: return false
if (!move.canBeUsed()) {
val gimmickMove = move.gimmickMove
val validGimmickMove = gimmickMove != null && !gimmickMove.disabled
if (!validGimmickMove && !move.canBeUsed()) {
// No PP or disabled or something
return false
}
val availableTargets = move.target.targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val availableTargets = (gimmickMove?.target ?: move.target).targetList(activeBattlePokemon)?.takeIf { it.isNotEmpty() } ?: return true
val pnx = targetPnx ?: return false // If the targets list is non-null then they need to have specified a target
val (_, targetPokemon) = activeBattlePokemon.actor.battle.getActorAndActiveSlotFromPNX(pnx)
@@ -124,22 +150,21 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
"move $moveIndex $digit"
} else {
"move $moveIndex"
}
}.plus(gimmickID?.let { " $gimmickID" } ?: "")
}
override fun saveToBuffer(buffer: PacketByteBuf) {
super.saveToBuffer(buffer)
buffer.writeString(moveName)
buffer.writeBoolean(targetPnx != null)
targetPnx?.let(buffer::writeString)
buffer.writeNullable(targetPnx) { _, targetPnx -> buffer.writeString(targetPnx) }
buffer.writeNullable(gimmickID) { _, gimmickID -> buffer.writeString(gimmickID) }
}
override fun loadFromBuffer(buffer: PacketByteBuf): ShowdownActionResponse {
super.loadFromBuffer(buffer)
moveName = buffer.readString()
if (buffer.readBoolean()) {
targetPnx = buffer.readString()
}
targetPnx = buffer.readNullable { buffer.readString() }
gimmickID = buffer.readNullable { buffer.readString() }
return this
}
}
@@ -243,11 +268,31 @@ class BagItemActionResponse(val bagItem: BagItem, val target: BattlePokemon, val
class ShowdownMoveset {
lateinit var moves: List<InBattleMove>
var trapped = false
var canMegaEvo = false
var canUltraBurst = false
var canZMove: List<InBattleGimmickMove?>? = null
var canDynamax = false
var maxMoves: List<InBattleGimmickMove?>? = null
var canTerastallize: String? = null
fun saveToBuffer(buffer: PacketByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, moves.size)
moves.forEach { it.saveToBuffer(buffer) }
buffer.writeBoolean(trapped)
buffer.writeBoolean(canMegaEvo)
buffer.writeBoolean(canUltraBurst)
buffer.writeNullable(canZMove) { _, canZMove ->
canZMove.forEach {
buffer.writeNullable(it) { _, zmove -> zmove.saveToBuffer(buffer)}
}
}
buffer.writeBoolean(canDynamax)
buffer.writeNullable(maxMoves) { _, maxMoves ->
maxMoves.forEach {
buffer.writeNullable(it) { _, maxMove -> maxMove.saveToBuffer(buffer)}
}
}
buffer.writeNullable(canTerastallize) { _, teraType -> buffer.writeString(teraType) }
}
fun loadFromBuffer(buffer: PacketByteBuf): ShowdownMoveset {
@@ -257,8 +302,60 @@ class ShowdownMoveset {
}
this.moves = moves
this.trapped = buffer.readBoolean()
this.canMegaEvo = buffer.readBoolean()
this.canUltraBurst = buffer.readBoolean()
this.canZMove = buffer.readNullable {
val zMoves = mutableListOf<InBattleGimmickMove?>()
repeat(moves.size) { zMoves.add(buffer.readNullable { InBattleGimmickMove.loadFromBuffer(buffer) }) }
return@readNullable zMoves
}
this.canDynamax = buffer.readBoolean()
this.maxMoves = buffer.readNullable {
val maxMoves = mutableListOf<InBattleGimmickMove?>()
repeat(moves.size) { maxMoves.add(buffer.readNullable { InBattleGimmickMove.loadFromBuffer(buffer) }) }
return@readNullable maxMoves
}
this.canTerastallize = buffer.readNullable{ buffer.readString() }
this.setGimmickMapping()
return this
}
/** Showdown IDs of battle gimmicks. */
enum class Gimmick(val id: String) {
MEGA_EVOLUTION("mega"),
ULTRA_BURST("ultra"),
Z_POWER("zmove"),
DYNAMAX("max"),
TERASTALLIZATION("terastal")
}
/** Check whether Dynamax/Gigantamax is already active. */
fun hasActiveGimmick() = !this.canDynamax && this.maxMoves != null
/** [Gimmick]s that can be activated with a [MoveActionResponse]. */
fun getGimmicks() = if (!hasActiveGimmick()) buildList{
if (canMegaEvo) add(Gimmick.MEGA_EVOLUTION)
if (canUltraBurst) add(Gimmick.ULTRA_BURST)
if (canZMove != null) add(Gimmick.Z_POWER)
if (canDynamax) add(Gimmick.DYNAMAX)
if (canTerastallize != null) add(Gimmick.TERASTALLIZATION)
}.toList() else listOf() // keep this immutable
/** Map each [InBattleMove] to their respective [InBattleGimmickMove] variant. */
fun setGimmickMapping() = (canZMove ?: maxMoves)?.let { gimmickMoves ->
moves.forEachIndexed { index, move -> move.gimmickMove = gimmickMoves[index] }
}
/** Sanitize Moveset by disabling gimmick. */
fun blockGimmick(gimmick: Gimmick) {
when (gimmick) {
Gimmick.MEGA_EVOLUTION -> this.canMegaEvo = false
Gimmick.DYNAMAX -> this.canDynamax = false
Gimmick.ULTRA_BURST -> this.canUltraBurst = false
Gimmick.Z_POWER -> this.canZMove = null
else -> this.canTerastallize = null
}
}
}
class ShowdownSide {
lateinit var name: UUID
@@ -320,4 +417,30 @@ class ShowdownPokemon {
ability = buffer.readString()
return this
}
}
/** Unwraps useless maxMoves object and initializes [ShowdownMoveset.gimmickMapping] */
object ShowdownMovesetAdapter : JsonDeserializer<ShowdownMoveset> {
val gson = GsonBuilder().addDeserializationExclusionStrategy(MovesetExclusionStrategy).create()
object MovesetExclusionStrategy : ExclusionStrategy {
override fun shouldSkipField(field: FieldAttributes?): Boolean {
return field?.name == "maxMoves"
}
override fun shouldSkipClass(p0: Class<*>?): Boolean {
return false;
}
}
override fun deserialize(jsonElement: JsonElement, type: Type, context: JsonDeserializationContext): ShowdownMoveset {
val json = jsonElement.asJsonObject
val moveset = gson.fromJson(json, ShowdownMoveset::class.java)
json.get("maxMoves")?.asJsonObject?.let { dynamaxOptions ->
val maxMovesToken = object: TypeToken<List<InBattleGimmickMove?>?>() {}.type
moveset.maxMoves = Gson().fromJson(dynamaxOptions.get("maxMoves"), maxMovesToken)
}
moveset.setGimmickMapping()
return moveset
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.battles
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.interpreter.*
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
@@ -22,6 +23,7 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.scheduling.after
import com.cobblemon.mod.common.api.text.*
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.battles.dispatch.BattleDispatch
import com.cobblemon.mod.common.battles.dispatch.DispatchResult
import com.cobblemon.mod.common.battles.dispatch.GO
@@ -107,6 +109,12 @@ object ShowdownInterpreter {
updateInstructions["|-sidestart|"] = this::handleSideStartInstructions
updateInstructions["|-sideend|"] = this::handleSideEndInstructions
updateInstructions["|-fieldactivate|"] = this::handleFieldActivateInstructions
updateInstructions["|-clearnegativeboost|"] = this::handleClearNegativeBoostInstructions
updateInstructions["|-zpower|"] = this::handleZPowerInstructions
updateInstructions["|-zbroken|"] = this::handleZBrokenInstructions
updateInstructions["|-terastallize|"] = this::handleTerastallizeInstructions
updateInstructions["|detailschange|"] = this::handleDetailsChangeInstructions
updateInstructions["|-mega|"] = this::handleMegaInstructions
sideUpdateInstructions["|request|"] = this::handleRequestInstruction
splitUpdateInstructions["|switch|"] = this::handleSwitchInstruction
@@ -147,7 +155,12 @@ object ShowdownInterpreter {
}
battle.dispatchWaiting(1.5F) {
battle.broadcastChatMessage(battleLang("$rootKey.$severity", pokemon.getName(), stat))
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("$rootKey.$severity.zeffect", pokemon.getName(), stat)
else -> battleLang("$rootKey.$severity", pokemon.getName(), stat)
}
battle.broadcastChatMessage(lang)
val boostBucket = if (isBoost) BattleContext.Type.BOOST else BattleContext.Type.UNBOOST
val context = getContextFromAction(message, boostBucket, battle)
// TODO: replace with context that tracks detailed information such as # of stages
@@ -645,10 +658,9 @@ object ShowdownInterpreter {
this.lastCauser[battle.battleId] = message
userPokemon.effectedPokemon.let { pokemon ->
val progress = UseMoveEvolutionProgress()
if (progress.shouldKeep(pokemon)) {
val created = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { progress }
created.updateProgress(UseMoveEvolutionProgress.Progress(created.currentProgress().move, created.currentProgress().amount + 1))
if (UseMoveEvolutionProgress.supports(pokemon, move)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is UseMoveEvolutionProgress && it.currentProgress().move == move }) { UseMoveEvolutionProgress() }
progress.updateProgress(UseMoveEvolutionProgress.Progress(move, progress.currentProgress().amount + 1))
}
}
@@ -814,7 +826,7 @@ object ShowdownInterpreter {
private fun handleRechargeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>){
battle.dispatchWaiting(2F){
val pokemon = message.getBattlePokemon(0, battle) ?: return@dispatchWaiting
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName() ?: ""))
battle.broadcastChatMessage(battleLang("recharge", pokemon.getName()))
battle.minorBattleActions[pokemon.uuid] = message
}
}
@@ -875,6 +887,7 @@ object ShowdownInterpreter {
val lang = when (effectID) {
"confusion", "perish3" -> return@dispatch GO // Skip
"perish2", "perish1", "perish0" -> battleLang("start.perish", pokemon.getName(), effectID.last().digitToInt())
"dynamax" -> battleLang("start.${message.effectAt(2)?.id ?: effectID}", pokemon.getName()).yellow()
else -> battleLang("start.$effectID", pokemon.getName())
}
battle.broadcastChatMessage(lang)
@@ -953,6 +966,7 @@ object ShowdownInterpreter {
battleLang("activate.forewarn", sourceName, moveName)
}
"focussash", "focusband" -> battleLang("activate.focusband", pokemonName, message.effectAt(1)!!.typelessData)
"maxguard", "protect" -> battleLang("activate.protect", pokemonName)
else -> battleLang("activate.${effect.id}", pokemonName, sourceName)
}
battle.broadcastChatMessage(lang)
@@ -1222,13 +1236,15 @@ object ShowdownInterpreter {
// Parse Json message and update state info for actor
val request = BattleRegistry.gson.fromJson(message.rawMessage.split("|request|")[1], ShowdownActionRequest::class.java)
request.sanitize(battle, battleActor)
if (battle.started) {
battle.dispatchGo {
// This request won't be acted on until the start of next turn
battleActor.sendUpdate(BattleQueueRequestPacket(request))
battleActor.request = request
battleActor.responses.clear()
// We need to send this out because 'upkeep' isn't received until the request is handled since the turn won't swap
if (request.forceSwitch.withIndex().any { it.value && battleActor.activePokemon.getOrNull(it.index)?.isGone() == false }) {
if (request.forceSwitch.contains(true)) {
battle.doWhenClear {
battleActor.mustChoose = true
battleActor.sendUpdate(BattleMakeChoicePacket())
@@ -1353,14 +1369,12 @@ object ShowdownInterpreter {
val battlePokemon = publicMessage.getBattlePokemon(0, battle) ?: return
if (privateMessage.optionalArgument("from")?.equals("recoil", true) == true) {
battlePokemon.effectedPokemon.let { pokemon ->
val recoilProgress = RecoilEvolutionProgress()
// Lazy cheat to see if it's necessary to use this
if (recoilProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { recoilProgress }
if (RecoilEvolutionProgress.supports(pokemon)) {
val newPercentage = privateMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toIntOrNull() ?: 0
val newHealth = (pokemon.hp * (newPercentage / 100.0)).roundToInt()
val difference = pokemon.currentHealth - newHealth
if (difference > 0) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is RecoilEvolutionProgress }) { RecoilEvolutionProgress() }
progress.updateProgress(RecoilEvolutionProgress.Progress(progress.currentProgress().recoil + difference))
}
}
@@ -1404,10 +1418,8 @@ object ShowdownInterpreter {
battlePokemon.effectedPokemon.currentHealth = remainingHealth
if (difference > 0) {
battlePokemon.effectedPokemon.let { pokemon ->
val damageProgress = DamageTakenEvolutionProgress()
// Lazy cheat to see if it's necessary to use this
if (damageProgress.shouldKeep(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { damageProgress }
if (DamageTakenEvolutionProgress.supports(pokemon)) {
val progress = pokemon.evolutionProxy.current().progressFirstOrCreate({ it is DamageTakenEvolutionProgress }) { DamageTakenEvolutionProgress() }
progress.updateProgress(DamageTakenEvolutionProgress.Progress(progress.currentProgress().amount + difference))
}
}
@@ -1432,8 +1444,8 @@ object ShowdownInterpreter {
* The switched Pokémon has HP HP, and status STATUS.
*/
fun handleDragInstruction(battle: PokemonBattle, actor: BattleActor, publicMessage: BattleMessage, privateMessage: BattleMessage) {
battle.dispatchGo {
val (pnx, pokemonID) = publicMessage.pnxAndUuid(0) ?: return@dispatchGo
battle.dispatchInsert {
val (pnx, pokemonID) = publicMessage.pnxAndUuid(0)!!
val (_, activePokemon) = battle.getActorAndActiveSlotFromPNX(pnx)
val pokemon = battle.getBattlePokemon(pnx, pokemonID)
@@ -1445,13 +1457,15 @@ object ShowdownInterpreter {
battle.majorBattleActions[pokemon.uuid] = publicMessage
val entity = if (actor is EntityBackedBattleActor<*>) actor.entity else null
battle.dispatch {
if (entity != null) {
this.createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon)
} else {
this.createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon)
setOf(
BattleDispatch {
if (entity != null) {
this.createEntitySwitch(battle, actor, entity, pnx, activePokemon, pokemon)
} else {
this.createNonEntitySwitch(battle, actor, pnx, activePokemon, pokemon)
}
}
}
)
}
}
@@ -1513,6 +1527,9 @@ object ShowdownInterpreter {
battlePokemon.heldItemManager.handleEndInstruction(battlePokemon, battle, message)
battle.minorBattleActions[battlePokemon.uuid] = message
battlePokemon.contextManager.remove(item.id, BattleContext.Type.ITEM)
if (message.hasOptionalArgument("eat")) {
battlePokemon.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F)
}
}
}
@@ -1527,15 +1544,16 @@ object ShowdownInterpreter {
val battlePokemon = privateMessage.getBattlePokemon(0, battle) ?: return
val rawHpAndStatus = privateMessage.argumentAt(1)?.split(" ") ?: return
val rawHpRatio = rawHpAndStatus.getOrNull(0) ?: return
val newHealth = rawHpRatio.split("/").getOrNull(0)?.toIntOrNull() ?: return
val newHealthRatio = publicMessage.argumentAt(1)?.split("/")?.getOrNull(0)?.toFloatOrNull()?.times(0.01F) ?: return
val newHealth = rawHpRatio.split("/").map { it.toFloatOrNull() ?: return }
val newHealthRatio = publicMessage.argumentAt(1)?.split("/")?.map { it.toFloatOrNull() ?: return } ?: return
val effect = privateMessage.effect()
val pokemonName = battlePokemon.getName()
broadcastOptionalAbility(battle, effect, pokemonName)
battle.dispatchWaiting {
if (pnx != null) {
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth.toFloat()), BattleHealthChangePacket(pnx, newHealthRatio))
// dynamax changes max health
battle.sendSidedUpdate(actor, BattleHealthChangePacket(pnx, newHealth[0], newHealth[1]), BattleHealthChangePacket(pnx, newHealthRatio[0], newHealthRatio[1]))
}
val silent = privateMessage.hasOptionalArgument("silent")
if (!silent) {
@@ -1570,7 +1588,7 @@ object ShowdownInterpreter {
battle.broadcastChatMessage(lang)
}
battle.minorBattleActions[battlePokemon.uuid] = privateMessage
battlePokemon.effectedPokemon.currentHealth = newHealth
battlePokemon.effectedPokemon.currentHealth = newHealth[0].toInt()
// This part is not always present
val rawStatus = rawHpAndStatus.getOrNull(1) ?: return@dispatchWaiting
@@ -1613,7 +1631,7 @@ object ShowdownInterpreter {
/**
* Format:
* |-clearallboost
* |-clearallboost|
*
* Clears all boosts from all Pokémon on both sides.
*/
@@ -1626,6 +1644,105 @@ object ShowdownInterpreter {
}
}
/**
* Format:
* |-clearnegativeboost|POKEMON
*
* Clear the negative boosts from the target Pokémon POKEMON (usually as the result of a zeffect).
*/
private fun handleClearNegativeBoostInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting(1.5F) {
val lang = when {
message.hasOptionalArgument("zeffect") -> battleLang("clearallnegativeboost.zeffect", pokemonName)
else -> battleLang("clearallnegativeboost", pokemonName)
}
battle.broadcastChatMessage(lang)
battlePokemon.contextManager.clear(BattleContext.Type.UNBOOST)
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-zpower|POKEMON
*
* The Pokémon POKEMON has used the z-move version of its move.
*/
private fun handleZPowerInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("zpower", pokemonName).yellow())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-zbroken|POKEMON
*
* A z-move has broken through protect and hit the Pokémon POKEMON.
*/
private fun handleZBrokenInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("zbroken", pokemonName).red())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-terastallize|POKEMON|TYPE
*
* The Pokémon POKEMON terastallized into type TYPE.
*/
private fun handleTerastallizeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
val type = message.effectAt(1)?.let { ElementalTypes.get(it.id) } ?: return
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("terastallize", pokemonName, type.displayName).yellow())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |detailschange|POKEMON|DETAILS|HP STATUS
*
* The specified Pokémon has changed formes (via Mega Evolution, ability, etc.). If the forme change is permanent,
* then detailschange will appear; otherwise, the client will send -formechange.
*/
private fun handleDetailsChangeInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
val formName = message.argumentAt(1)?.split(',')?.get(0)?.substringAfter('-')?.lowercase() ?: return
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("detailschange.$formName", pokemonName))
battle.majorBattleActions[battlePokemon.uuid] = message
}
}
/**
* Format:
* |-mega|POKEMON|MEGASTONE
*
* The Pokémon POKEMON used MEGASTONE to Mega Evolve.
*/
private fun handleMegaInstructions(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
val battlePokemon = message.getBattlePokemon(0, battle) ?: return
val pokemonName = battlePokemon.getName()
battle.dispatchWaiting {
battle.broadcastChatMessage(battleLang("mega", pokemonName).yellow())
battle.minorBattleActions[battlePokemon.uuid] = message
}
}
// Used for things that are only meant for visual information we don't have
private fun handleSilently(battle: PokemonBattle, message: BattleMessage, remainingLines: MutableList<String>) {
battle.dispatchGo { }
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.battles.pokemon
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
import com.cobblemon.mod.common.api.moves.MoveSet
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemManager
@@ -89,4 +90,8 @@ open class BattlePokemon(
fun isSentOut() = actor.battle.activePokemon.any { it.battlePokemon == this }
fun canBeSentOut() = !isSentOut() && !willBeSwitchedIn && health > 0
fun writeVariables(struct: VariableStruct) {
effectedPokemon.writeVariables(struct)
}
}
@@ -0,0 +1,56 @@
/*
* 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.berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.BlockState
import net.minecraft.predicate.NumberRange
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
import net.minecraft.world.biome.Biome
/**
* A [GrowthFactor] that is active based on the downfall property of a [Biome].
*
* @property range The range of possible [Biome.getDownfall] values.
* @property bonusYield The range of possible bonus values.
*
* @author Licious
* @since December 2nd, 2022
*/
class BiomeDownfallGrowthFactor(
val range: NumberRange.FloatRange,
val bonusYield: IntRange
) : GrowthFactor {
override fun validateArguments() {
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
throw IllegalArgumentException("$ID bonusYield must be a positive range")
}
}
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
val biome = world.getBiome(pos).value()
return this.range.test(biome.weather.downfall.toDouble())
}
override fun yield() = this.bonusYield.random()
override fun minYield() = this.bonusYield.first
override fun maxYield() = this.bonusYield.last
companion object {
val ID = cobblemonResource("biome_downfall")
}
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.registry.BiomeIdentifierCondition
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.BlockState
import net.minecraft.registry.RegistryKeys
import net.minecraft.registry.tag.TagKey
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
import net.minecraft.world.biome.Biome
class PreferredBiomeGrowthFactor(
val bonusYield: IntRange
) : GrowthFactor{
override fun validateArguments() {
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
throw IllegalArgumentException("$ID bonusYield must be a positive range")
}
}
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
val biome = world.getBiome(pos)
val block = state.block as BerryBlock
val biomeTags = block.berry()?.preferredBiomeTags ?: emptyList()
return biomeTags.any { biome.isIn(it) }
}
override fun yield() = this.bonusYield.random()
override fun minYield() = this.bonusYield.first
override fun maxYield() = this.bonusYield.last
companion object {
val ID = cobblemonResource("preferred_biome")
}
}
@@ -0,0 +1,56 @@
/*
* 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.berry
import com.cobblemon.mod.common.api.berry.GrowthFactor
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.block.BlockState
import net.minecraft.predicate.NumberRange
import net.minecraft.util.math.BlockPos
import net.minecraft.world.WorldView
import net.minecraft.world.biome.Biome
/**
* A [GrowthFactor] that is active based on the temperature property of a [Biome].
*
* @property range The range of possible [Biome.getTemperature] values.
* @property bonusYield The range of possible bonus values.
*
* @author Licious
* @since December 2nd, 2022
*/
class BiomeTemperatureGrowthFactor(
val range: NumberRange.FloatRange,
val bonusYield: IntRange
) : GrowthFactor {
override fun validateArguments() {
if (this.bonusYield.first < 0 || this.bonusYield.last < 0) {
throw IllegalArgumentException("$ID bonusYield must be a positive range")
}
}
override fun isValid(world: WorldView, state: BlockState, pos: BlockPos): Boolean {
val biome = world.getBiome(pos).value()
return this.range.test(biome.temperature.toDouble())
}
override fun yield() = this.bonusYield.random()
override fun minYield() = this.bonusYield.first
override fun maxYield() = this.bonusYield.last
companion object {
val ID = cobblemonResource("biome_temperature")
}
}
@@ -137,7 +137,7 @@ class ApricornBlock(settings: Settings, val apricorn: Apricorn) : HorizontalFaci
world.playSoundServer(position = pos.toVec3d(), sound = SoundEvents.ENTITY_ITEM_PICKUP, volume = 0.7F, pitch = 1.4F)
if (world is ServerWorld && player is ServerPlayerEntity) {
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, world, pos))
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, apricorn, world, pos))
}
}
return ActionResult.success(world.isClient)
@@ -0,0 +1,303 @@
/*
* 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.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.berry.Berry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.berry.BerryMutationOfferEvent
import com.cobblemon.mod.common.api.events.berry.BerryMutationResultEvent
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.entity.BerryBlockEntity
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.Fertilizable
import net.minecraft.block.ShapeContext
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityTicker
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.item.ShovelItem
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.EnumProperty
import net.minecraft.state.property.IntProperty
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Box
import net.minecraft.util.math.Direction
import net.minecraft.util.math.random.Random
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
class BerryBlock(private val berryIdentifier: Identifier, settings: Settings) : BlockWithEntity(settings), Fertilizable, Mulchable {
private val lookupDirections = setOf(Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH)
/**
* Returns the [Berry] behind this block,
* This will be null if it doesn't exist in the [Berries] registry.
*
* @return The [Berry] if existing.
*/
fun berry(): Berry? = Berries.getByIdentifier(this.berryIdentifier)
override fun createBlockEntity(pos: BlockPos, state: BlockState) = BerryBlockEntity(pos, state, berryIdentifier)
override fun isFertilizable(world: WorldView, pos: BlockPos, state: BlockState, isClient: Boolean) = !this.isMaxAge(state)
override fun canGrow(world: World, random: Random, pos: BlockPos, state: BlockState) = !this.isMaxAge(state)
override fun <T : BlockEntity> getTicker(world: World, blockState: BlockState, blockWithEntityType: BlockEntityType<T>): BlockEntityTicker<T>? = checkType(blockWithEntityType, CobblemonBlockEntities.BERRY, BerryBlockEntity.TICKER)
init {
defaultState = this.stateManager.defaultState
.with(WAS_GENERATED, false)
.with(AGE, 0)
.with(MULCH, MulchVariant.NONE)
}
override fun grow(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState) {
val curAge = state.get(AGE)
val newAge = curAge + 1
if (newAge > FRUIT_AGE) return
val newState = state.with(AGE, newAge)
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (curAge == MATURE_AGE) {
determineMutation(world, random, pos, state)
treeEntity.generateGrowthPoints(world, state, pos, null)
}
world.setBlockState(pos, newState, Block.NOTIFY_LISTENERS)
treeEntity.goToNextStageTimer(FRUIT_AGE - curAge)
treeEntity.markDirty()
}
fun determineMutation(world: World, random: Random, pos: BlockPos, state: BlockState) {
val mutations = hashSetOf<Berry>()
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
for (direction in this.lookupDirections) {
val redirectedPos = pos.add(direction.vector)
val redirectedState = world.getBlockState(redirectedPos)
val berryBlock = redirectedState.block as? BerryBlock ?: continue
val berry = berryBlock.berry() ?: continue
val mutation = this.berry()?.mutationWith(berry) ?: continue
mutations += mutation
}
this.berry()?.let { berry ->
CobblemonEvents.BERRY_MUTATION_OFFER.post(BerryMutationOfferEvent(berry, world, state, pos, mutations)) { berryMutationOffer ->
if (berryMutationOffer.mutations.isNotEmpty()) {
var mutateChance = 125
if (getMulch(state) == MulchVariant.SURPRISE) {
mutateChance *= 4
treeEntity.decrementMulchDuration(world, pos, state)
}
val mutation = if (random.nextInt(1000) < mutateChance) mutations.random() else null
(world.getBlockEntity(pos) as? BerryBlockEntity)?.let { blockEntity ->
CobblemonEvents.BERRY_MUTATION_RESULT.post(BerryMutationResultEvent(berry, world, state, pos, berryMutationOffer.mutations, mutation)) { berryMutationResult ->
berryMutationResult.pickedMutation?.let { mutation -> blockEntity.mutate(mutation) }
}
}
}
}
}
}
override fun canHaveMulchApplied(
world: ServerWorld,
pos: BlockPos,
state: BlockState,
variant: MulchVariant
): Boolean {
return getMulch(state) == MulchVariant.NONE && state.get(AGE) < FLOWER_AGE && world.getBlockState(pos.down()).isOf(Blocks.FARMLAND)
}
override fun applyMulch(
world: ServerWorld,
random: Random,
pos: BlockPos,
state: BlockState,
variant: MulchVariant
) {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
setMulch(world, pos, state, variant)
treeEntity.mulchDuration = variant.duration
world.playSound(null, pos, CobblemonSounds.MULCH_PLACE, SoundCategory.BLOCKS, 0.6F, 1F)
}
@Deprecated("Deprecated in Java")
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (player.getStackInHand(hand).item is ShovelItem && getMulch(state) != MulchVariant.NONE) {
setMulch(world, pos, state, MulchVariant.NONE)
treeEntity.markDirty()
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundCategory.BLOCKS, 0.6F, 1F)
this.spawnBreakParticles(world, player, pos, state.with(AGE, 0))
return ActionResult.SUCCESS
}
if (player.getStackInHand(hand).isOf(Items.BONE_MEAL) && !this.isMaxAge(state)) {
return ActionResult.PASS
} else if (this.isMaxAge(state)) {
val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return ActionResult.PASS
blockEntity.harvest(world, state, pos, player).forEach { drop ->
Block.dropStack(world, pos, drop)
}
world.playSound(null, pos, CobblemonSounds.BERRY_HARVEST, SoundCategory.BLOCKS, 0.4F, 1F)
return ActionResult.success(world.isClient)
}
return super.onUse(state, world, pos, player, hand, hit)
}
@Deprecated("Deprecated in Java")
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
val below = world.getBlockState(pos.down())
return (state.get(WAS_GENERATED) && below.isIn(CobblemonBlockTags.BERRY_WILD_SOIL)) || below.isIn(CobblemonBlockTags.BERRY_SOIL)
}
@Deprecated("Deprecated in Java")
override fun getStateForNeighborUpdate(state: BlockState, direction: Direction, neighborState: BlockState, world: WorldAccess, pos: BlockPos, neighborPos: BlockPos): BlockState {
return if (state.canPlaceAt(world, pos)) super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos) else Blocks.AIR.defaultState
}
override fun onPlaced(world: World, pos: BlockPos, state: BlockState, placer: LivingEntity?, itemStack: ItemStack) {
// if (!world.isClient) {
// val blockEntity = world.getBlockEntity(pos) as? BerryBlockEntity ?: return
// blockEntity.generateGrowthPoints(world, state, pos, placer)
// }
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(AGE)
builder.add(WAS_GENERATED)
builder.add(MULCH)
}
override fun getPickStack(world: BlockView?, pos: BlockPos?, state: BlockState?): ItemStack {
val berryItem = this.berry()?.item() ?: return ItemStack.EMPTY
return ItemStack(berryItem)
}
@Deprecated("Deprecated in Java")
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape {
val berry = this.berry() ?: return VoxelShapes.fullCube()
return when (state.get(AGE)) {
0 -> PLANTED_SHAPE
1 -> PLANTED_SHAPE
2 -> berry.sproutShape
else -> berry.matureShape
}
}
private fun isMaxAge(state: BlockState) = state.get(AGE) == FRUIT_AGE
@Deprecated("Deprecated in Java")
override fun getRenderType(blockState: BlockState) = BlockRenderType.MODEL
companion object {
const val MATURE_AGE = 3
const val FLOWER_AGE = 4
const val FRUIT_AGE = 5
val AGE: IntProperty = IntProperty.of("age", 0, FRUIT_AGE)
val MULCH: EnumProperty<MulchVariant> = EnumProperty.of("mulch", MulchVariant::class.java)
val WAS_GENERATED: BooleanProperty = BooleanProperty.of("generated")
// val PLANTED_SHAPE = VoxelShapes.union(
// VoxelShapes.cuboid(0.3125, -0.0625, 0.3125, 0.6875, 0.0, 0.6875),
// VoxelShapes.cuboid(0.375, 0.0, 0.375, 0.625, 0.0625, 0.625)
// )
val PLANTED_SHAPE = VoxelShapes.cuboid(0.0, -0.1, 0.0, 1.0, 0.25, 1.0)
val STANDARD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val STANDARD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 25.0, 16.0))
val SHORT_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 12.0, 16.0))
val SHORT_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val VOLCANO_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
val VOLCANO_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val NEST_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
val NEST_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 20.0, 16.0))
val FRILL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 8.0, 16.0))
val FRILL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 14.0, 16.0))
val BLOCK_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 17.0, 16.0))
val BLOCK_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val PYRAMID_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 17.0, 16.0))
val PYRAMID_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val TAIL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val TAIL_MATURE = listOf(Box(0.0, 1.0, 0.0, 16.0, 24.0, 16.0))
val SWORD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 5.0, 16.0))
val SWORD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val PLATFORM_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 7.0, 16.0))
val PLATFORM_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 22.0, 16.0))
val STAND_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 13.0, 16.0))
val STAND_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val CONE_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val CONE_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 23.0, 16.0))
val SQUAT_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 12.0, 16.0))
val SQUAT_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 22.0, 16.0))
val LANTERN_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 20.0, 16.0))
val LANTERN_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val BOX_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val BOX_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
val BLOSSOM_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 4.0, 16.0))
val BLOSSOM_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 6.0, 16.0))
val LILYPAD_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 11.0, 16.0))
val LILYPAD_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val TALL_SPROUT = listOf(Box(0.0, -1.0, 0.0, 16.0, 16.0, 16.0))
val TALL_MATURE = listOf(Box(0.0, -1.0, 0.0, 16.0, 24.0, 16.0))
fun getMulch(state: BlockState): MulchVariant {
if (!state.contains(MULCH)) {
return MulchVariant.NONE
}
return state.get(MULCH)
}
fun setMulch(world: World, pos: BlockPos, state: BlockState, mulch: MulchVariant) {
world.setBlockState(pos, state.with(MULCH, mulch))
}
}
}
@@ -25,7 +25,6 @@ import net.minecraft.world.World
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
class BigRootBlock(settings: Settings) : RootBlock(settings) {
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AABB
override fun onUse(state: BlockState, world: World, pos: BlockPos, player: PlayerEntity, hand: Hand, hit: BlockHitResult): ActionResult {
@@ -50,7 +50,7 @@ class MedicinalLeekBlock(settings: Settings) : CropBlock(settings) {
// These 3 are still around for the sake of compatibility, vanilla won't trigger it but some mods might
// We implement applyGrowth & getGrowthAmount for them
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = false
override fun isFertilizable(world: WorldView?, pos: BlockPos?, state: BlockState?, isClient: Boolean): Boolean = !this.isMature(state)
override fun applyGrowth(world: World, pos: BlockPos, state: BlockState) {
world.setBlockState(pos, state.with(this.ageProperty, (this.getAge(state) + 1).coerceAtMost(this.maxAge)), NOTIFY_LISTENERS)
@@ -260,7 +260,7 @@ class PCBlock(properties: Settings): BlockWithEntity(properties), Waterloggable
world.playSoundServer(
position = blockPos.toVec3d(),
sound = CobblemonSounds.PC_ON,
volume = 1F,
volume = 0.5F,
pitch = 1F
)
return ActionResult.SUCCESS
@@ -21,39 +21,48 @@ import com.cobblemon.mod.common.util.isInBattle
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import com.cobblemon.mod.common.util.voxelShape
import net.minecraft.block.*
import java.util.UUID
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.block.Waterloggable
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BlockEntityTicker
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.state.StateManager
import net.minecraft.util.*
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.EnumProperty
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
import java.util.*
import net.minecraft.entity.LivingEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemStack
import net.minecraft.server.world.ServerWorld
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.EnumProperty
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.StringIdentifiable
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.explosion.Explosion
@Suppress("OVERRIDE_DEPRECATION", "DEPRECATION")
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable {
class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterloggable, PreEmptsExplosion {
companion object {
val PART: EnumProperty<PasturePart> = EnumProperty.of("part", PasturePart::class.java)
val ON: BooleanProperty = BooleanProperty.of("on")
@@ -145,7 +154,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
}
fun getPositionOfOtherPart(state: BlockState, pos: BlockPos): BlockPos {
return if (state.get(PART) == PasturePart.BOTTOM) {
return if (state.contains(PART) && state.get(PART) == PasturePart.BOTTOM) {
pos.up()
} else {
pos.down()
@@ -160,7 +169,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
}
}
private fun isBase(state: BlockState): Boolean = state.get(PART) == PasturePart.BOTTOM
private fun isBase(state: BlockState): Boolean = state.contains(PART) && state.get(PART) == PasturePart.BOTTOM
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
@@ -186,6 +195,11 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
blockEntity.onBroken()
}
override fun whenExploded(world: World?, state: BlockState, pos: BlockPos?) {
val blockEntity = world?.getBlockEntity(pos) as? PokemonPastureBlockEntity ?: return
blockEntity.onBroken()
}
override fun <T : BlockEntity?> getTicker(world: World, state: BlockState, type: BlockEntityType<T>): BlockEntityTicker<T>? {
return checkType(type, CobblemonBlockEntities.PASTURE, PokemonPastureBlockEntity.TICKER::tick)
}
@@ -246,7 +260,7 @@ class PastureBlock(properties: Settings): BlockWithEntity(properties), Waterlogg
world.playSoundServer(
position = pos.toVec3d(),
sound = CobblemonSounds.PC_ON,
volume = 1F,
volume = 0.5F,
pitch = 1F
)
@@ -0,0 +1,17 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import net.minecraft.block.BlockState
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
interface PreEmptsExplosion {
fun whenExploded(world: World?, state: BlockState, pos: BlockPos?)
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.mulch.Mulchable
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.block.CropBlock
import net.minecraft.block.ShapeContext
import net.minecraft.item.ItemConvertible
@@ -50,8 +51,9 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
val floor = world.getBlockState(pos.down())
val block = world.getBlockState(pos)
// A bit of a copy pasta but we don't have access to the BlockState being attempted to be placed above on the canPlantOnTop
return (world.getBaseLightLevel(pos, 0) >= 8 || world.isSkyVisible(pos)) && ((state.get(IS_WILD) && floor.isIn(BlockTags.DIRT)) || this.canPlantOnTop(floor, world, pos))
return (block.isIn(BlockTags.REPLACEABLE_BY_TREES) || block.isOf(Blocks.AIR) || block.isOf(this)) && ((state.get(IS_WILD) && floor.isIn(BlockTags.DIRT)) || this.canPlantOnTop(floor, world, pos))
}
override fun getOutlineShape(state: BlockState, world: BlockView, pos: BlockPos, context: ShapeContext): VoxelShape = AGE_SHAPES.getOrNull(this.getAge(state)) ?: VoxelShapes.fullCube()
@@ -63,7 +65,7 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
override fun applyMulch(world: ServerWorld, random: Random, pos: BlockPos, state: BlockState, variant: MulchVariant) {
val picked = Mutation.values().filterNot { it == Mutation.NONE }.random()
world.setBlockState(pos, state.with(MUTATION, picked).with(AGE, MUTABLE_MAX_AGE + 1))
world.setBlockState(pos, state.with(MUTATION, picked))//.with(AGE, MUTABLE_MAX_AGE + 1))
}
/**
@@ -133,6 +135,7 @@ class RevivalHerbBlock(settings: Settings) : CropBlock(settings), Mulchable {
val AGE = IntProperty.of("age", MIN_AGE, MAX_AGE)
val IS_WILD = BooleanProperty.of("is_wild")
val MUTATION = EnumProperty.of("mutation", Mutation::class.java)
// val MULCH = EnumProperty.of("mulch", MulchVariant::class.java)
/**
* The [VoxelShape] equivalent to a certain age.

Some files were not shown because too many files have changed in this diff Show More