Compare commits

..
Author SHA1 Message Date
Polymeta 367e84a9c8 26.1.2 backport of dataregistry
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-05-22 11:16:36 +01:00
Polymeta c04345a789 Merge branch 'autocorrect' into 'main'
autocorrect

See merge request cable-mc/cobblemon!2284
2026-05-22 08:07:26 +00:00
Sterrezo 93e84e6ffd autocorrect 2026-05-22 09:17:24 +02:00
Polymeta cc6305bfec Merge branch 'qa-63' into 'main'
Fixed QA 63 for 1.8 / Ally-move turn skip

See merge request cable-mc/cobblemon!2283
2026-05-21 09:54:41 +00:00
CraterandPolymeta 7d38efaa50 Fixed QA 63 for 1.8 / Ally-move turn skip 2026-05-21 09:54:40 +00:00
Polymeta 9a3f5a605a Merge branch 'fix/particle-shader' into 'main'
Fix use the default minecraft shader instead of our own ( don't want to make...

See merge request cable-mc/cobblemon!2282
2026-05-21 09:51:30 +00:00
terrainwax b9922057ce Fix use the default minecraft shader instead of our own ( don't want to make the custom pipeline for Iris just for a 0.5 cutout XD ) 2026-05-21 10:58:11 +02:00
Polymeta d9c765ca53 Merge branch 'hydrapple-fix' into 'main'
Zero scaled locators don't get alpha eye effects, fixes hydrapple

See merge request cable-mc/cobblemon!2280
2026-05-21 08:39:49 +00:00
Polymeta 96c3f7764e Merge branch 'CloseSummary' into 'main'
Added ability to close summary and starter screen with summary button.

See merge request cable-mc/cobblemon!2274
2026-05-21 08:38:55 +00:00
Polymeta 255930751a Merge branch '1.8-qa-fixes-#3' into 'main'
Prevent Tera Blast from getting chosen when rolling for TM moves in general...

Closes #58

See merge request cable-mc/cobblemon!2278
2026-05-21 08:38:22 +00:00
Polymeta d3f32ee645 Merge branch 'mimic-block-json-list-additions' into 'main'
Mimic block json list additions

See merge request cable-mc/cobblemon!2281
2026-05-21 08:37:40 +00:00
Slippy e444206a55 and again 2026-05-21 02:51:30 -05:00
Slippy e58acd646f spelling & grammatical errors fixed 2026-05-21 02:45:25 -05:00
Slippy bdcc05aa78 added a bunch of blocks to the habitat block mimic list 2026-05-21 02:07:20 -05:00
Hiroku bb899118ef Merge branch '1.8-qa-fixes-#4' into 'main'
1.8 qa fixes #4

Closes #4

See merge request cable-mc/cobblemon!2279
2026-05-21 06:40:37 +00:00
Paul 71585174c0 Fixed an inconveniently small input max length for mimic block id 2026-05-21 16:39:44 +10:00
Paul 3449c98b56 Zero scaled locators don't get alpha eye effects, fixes hydrapple 2026-05-21 13:53:20 +10:00
plastered_crab 60aa991d55 Special moves are no longer searchable in the Search by Move Name function of the dex 2026-05-20 16:42:44 -07:00
plastered_crab 8da0fc34c6 Alphas drop rewards based on their level 2026-05-20 15:41:05 -07:00
plastered_crab 41c55d80a8 "Search By Drops" works for Seen Pokemon now too 2026-05-20 14:05:01 -07:00
plastered_crab 09dfbccb8e Legacy moves are no longer searchable in the Search by Move Name function of the dex 2026-05-20 13:53:19 -07:00
plastered_crab a66d263dd8 Add langs for Data Monitor Shiny and Alpha chance for Config 2026-05-20 12:33:06 -07:00
plastered_crab 5a929e3901 Prevent Tera Blast from getting chosen when rolling for TM moves in general (also for alphas getting the move) 2026-05-20 12:25:03 -07:00
Polymeta d95db24426 bump packsquash
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-05-20 11:54:29 +01:00
Hiroku a64cbb7e8a Merge branch '1.8-more-qa-fixea' into 'main'
- Show SEEN pokemon in dex filters for Rideable and Learnable TM Moves

See merge request cable-mc/cobblemon!2277
2026-05-20 10:52:10 +00:00
Paul e2ba1dc575 Cleanup for pokedex GUI stuff 2026-05-20 20:01:56 +10:00
Polymeta f7e381924e Merge branch 'sneasler-ride-fix' into 'main'
Fixed basket sneasler causing crashes upon riding

See merge request cable-mc/cobblemon!2276
2026-05-20 09:43:30 +00:00
plastered_crab f4c2a5197c why can't I create an MR 2026-05-20 01:41:33 -07:00
Paul 361a8fcbe1 Fixed basket sneasler causing crashes upon riding 2026-05-20 13:18:05 +10:00
Polymeta e179e044ef Merge branch 'fix-cobblemon-boat-renderer' into 'main'
Fix cobblemon boat renderer

See merge request cable-mc/cobblemon!2275
2026-05-19 15:53:50 +00:00
Faintful f55d8737ba Add changelog 2026-05-18 19:57:00 +02:00
Faintful 336180656e Fix Cobblemon boat renderer registration for non-chest boats 2026-05-18 18:37:46 +02:00
Paul Freck 620589681d Added ability to close summary and starter screen with summary button. 2026-05-18 05:44:12 -04:00
Polymeta 8359b1ade1 Merge branch 'complex-spawn-buckets' into 'main'
Rebuilt spawn buckets to be controlled by the spawner, allowing new buckets in specific contexts, and flattened habitat configuration classes

See merge request cable-mc/cobblemon!2267
2026-05-17 23:54:13 +00:00
Hiroku 8018c1142a Merge branch 'ability-capsule-returns-empty-bottle' into 'main'
Add conditional giveOrDropItemStack() call for returning empty bottles

See merge request cable-mc/cobblemon!2255
2026-05-17 06:56:27 +00:00
Hiroku cfca238573 Merge branch 'data-compatibility' into 'main'
Some Mod Compatibility

See merge request cable-mc/cobblemon!2269
2026-05-17 06:56:01 +00:00
Hiroku e3cf367e45 Merge branch 'riding-when-not-supposed-to-fix' into 'main'
Same fix for mounting a recalling a pokemon, but better

See merge request cable-mc/cobblemon!2268
2026-05-17 06:54:19 +00:00
Hiroku b9b01af451 Merge branch 'fix/fabric-publication-pom' into 'main'
Fix publication pom to include transitive dependencies now in JIJ

See merge request cable-mc/cobblemon!2271
2026-05-17 06:51:39 +00:00
terrainwax c68f637b70 Fix publication pom to include transitive dependencies now in JIJ 2026-05-16 21:23:27 +02:00
Polymeta d44126179a Merge branch 'fix/neoforge-shader-init' into 'main'
Fix Neoforge shader initialization

See merge request cable-mc/cobblemon!2270
2026-05-16 17:37:47 +00:00
terrainwax 432b350d98 Fix Neoforge shader initialization 2026-05-16 18:45:48 +02:00
Faintful 26677420dd Add changelog 2026-05-16 11:55:48 +02:00
dhi_holo a1487e4ee5 Merge branch 'main' into data-compatibility 2026-05-16 19:46:53 +10:00
Sterrezo 78b2a7d721 Same fix for mounting a recalling a pokemon, but better 2026-05-16 11:42:34 +02:00
dhi_holo d4a3dfdb07 Phase 1 Complete
Added basic support for Create, Supplementaries/Supplementaries Squared/Amendments/Moonlight, Arts and Crafts, and Farmer's Delight (NeoForge and Fabric) in a variety of ways
2026-05-16 19:18:25 +10:00
Paul 0f3dcb760e Rebuilt spawn buckets to be controlled by the spawner, allowing new buckets in specific contexts, and flattened habitat configuration classes 2026-05-16 16:34:20 +10:00
Hiroku 0e1b59fb25 Merge branch 'riding-when-not-supposed-to-fix' into 'main'
Prevent riding when Pokemon is being sent out or retrieved

See merge request cable-mc/cobblemon!2266
2026-05-16 01:09:42 +00:00
Sterrezo 3565015039 Remove the ability to send one self into the void and crash the game 2026-05-16 02:36:56 +02:00
Hiroku 218b90860b Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2264
2026-05-15 23:03:46 +00:00
Polymeta a59573073f Merge branch 'sterr/sound' into 'main'
TMM and TM sound implementation + miscellaneous sound tweaks

See merge request cable-mc/cobblemon!2265
2026-05-15 21:52:17 +00:00
Sterrezo 6475d7d732 Fixed TM items playing unintended sounds 2026-05-15 23:38:37 +02:00
Sterrezo c467e98345 Update sounds.json 2026-05-15 21:04:47 +02:00
Sterrezo 6b206cfaaf TM Machine now stops playing sounds when broken 2026-05-15 20:57:13 +02:00
Sterrezo 8aac4c294f More TMM work and new TM sounds 2026-05-15 19:36:58 +02:00
Sterrezo 2e330076b8 Merge branch 'main' into sterr/sound 2026-05-15 18:20:57 +02:00
Jerzykv e1cd217290 Hisuian Zoroark fixes 2026-05-15 18:05:33 +02:00
dhi_holo 60f00ca34d Merge branch 'main' into data-compatibility 2026-05-16 00:10:15 +10:00
dhi_holo 416a2ac390 Create Farmer's Split
Added first pass of inbuilt support for Create, and split Farmer's Delight compat between two jsons for proper compatibility on the two loaders
2026-05-16 00:08:36 +10:00
Polymeta 690ada8271 Merge branch 'my-goodness-more-fixes' into 'main'
There are more damn 1.8 QA fixes

See merge request cable-mc/cobblemon!2256
2026-05-15 08:27:44 +00:00
Polymeta b1b06f19b2 Merge branch 'click-to-cry' into 'main'
Play Pokemon cries from summary screen

See merge request cable-mc/cobblemon!2252
2026-05-15 08:25:30 +00:00
dhi_holo 5bc41245ad Merge branch 'main' into data-compatibility 2026-05-15 15:49:52 +10:00
Paul 490fa91ce7 Removed fossil structure tag as we don't use structures for fossil stuff anymore 2026-05-15 14:18:17 +10:00
Gertimoshka 8606d6928d Changes and Changelog 2026-05-15 07:08:09 +03:00
Hiroku 5897b43564 Merge branch 'sounds-unlaggifier' into 'main'
Fix specific quirk of CancelableSoundInstance causing lagspike when far away from a loaded block that creates it

See merge request cable-mc/cobblemon!2248
2026-05-15 03:47:48 +00:00
Gertimoshka 9fd8d32215 Changelog 2026-05-15 06:46:34 +03:00
dhi_holo 03c278229b Merge branch 'main' into data-compatibility 2026-05-15 13:34:54 +10:00
dhi_holo 8ce3322105 New Screen Who This 2026-05-15 13:34:40 +10:00
Paul 364d084584 Small miscellaneous touchups including from thetechgo's MR 2026-05-15 13:01:26 +10:00
Paul 916e6de1d2 Fixed client's "sound" function not taking a volume property as the second argument but requiring one to specify pitch (whack) 2026-05-15 12:03:21 +10:00
ewwww 0b9e706a36 NIDO NIDO BEAMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM 2026-05-14 21:57:38 -04:00
Paul 6d4f529fc0 Some lang updates in changelog 2026-05-15 11:54:50 +10:00
Paul 015de548d3 Merge remote-tracking branch 'origin/main' 2026-05-15 11:53:11 +10:00
Paul 04ca03b3a5 Merge remote-tracking branch 'origin/lang-updates' 2026-05-15 11:53:01 +10:00
Hiroku c7c145ab78 Merge branch 'scrolling-texture-emissive-fix' into 'main'
Fixed scrolling texture layer not working with emissive.

See merge request cable-mc/cobblemon!2260
2026-05-15 01:48:31 +00:00
JadedArts a376863953 One last tweak to Draco Meteor's visuals 2026-05-14 12:28:32 -05:00
Sterrezo 2e68c3fc44 Lowered the volume of Zebstrika's cry 2026-05-14 16:53:49 +02:00
Sterrezo 36f2eee7f9 Oh right, the sounds.json 2026-05-14 16:52:33 +02:00
Sterrezo 71ac29ba8b Merge branch 'implement_pokemon' of https://gitlab.com/cable-mc/cobblemon into implement_pokemon 2026-05-14 16:48:55 +02:00
Sterrezo 781c82230e I want to get off Draco Meteor's wild ride 2026-05-14 16:48:33 +02:00
Jerzykv c58d49ebab fixed hisuian samurott exploding the game 2026-05-14 16:39:49 +02:00
dhi_holo fd8d5630f4 Ya Like Compatibility? 2026-05-14 21:28:28 +10:00
Jerzykv a86b11fdd5 implemented alpha eyes/locators of Paldea 2026-05-14 13:12:09 +02:00
Jerzykv 829dca9d99 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-14 12:43:04 +02:00
Jerzykv 0f0368f3bd Maushold fixes 2026-05-14 12:42:49 +02:00
ewwww 6533030d36 oh brother 2026-05-14 06:21:38 -04:00
ewwww 0efb0d9767 remaining gen 1 eye locators 2026-05-14 06:15:17 -04:00
Paul 8151f9b3e6 Handle the scraping of multipliers more dynamically too, using the Dictionary tab 2026-05-14 20:11:24 +10:00
RedGenesectNinja 4e8ffd3558 Fixed Bidoof's cry not playing a sound 2026-05-14 03:31:41 -05:00
Paul b88873fbc3 Slight upgrade to herds that they support leader-only entries, added scraper for herd data, deleted example herd 2026-05-14 18:10:17 +10:00
RedGenesectNinja dba1ab26a5 I forgot to implement Bidoof's new animations 2026-05-14 02:53:22 -05:00
ewwww cc2295d629 updated models with locators for maushold, added alpha eyes for veluza and dondozo 2026-05-14 03:26:48 -04:00
ewwww 4d3d8d1607 updated models with (eye) locators for:
poliwag, poliwhirl, poliwrath, machop, machoke, machamp, bellsprout, weepinbell, victreebel, tentacool, geodude, graveler, golem, ponyta, rapidash, slowpoke, slowbro, farfetch'd, doduo, dodrio, seel, grimer, muk, shellder, cloyster, gastly, haunter, onix, krabby, kingler, voltorb, electrode, and exeggutor
2026-05-14 03:26:20 -04:00
RedGenesectNinja 1d09360844 Implemented some Gen 4 alpha eyes and remodels for Bidoof and Cranidos 2026-05-14 01:03:46 -05:00
Polymeta 079e75d828 Merge branch 'alpha-moveset-fix' into 'main'
Fixed alpha pokemon not using full movesets

See merge request cable-mc/cobblemon!2263
2026-05-13 10:10:51 +00:00
Paul a3567ead04 Merge remote-tracking branch 'origin/main' into alpha-moveset-fix 2026-05-13 18:18:24 +10:00
Paul f4fef2855b Fixed alpha pokemon not using full movesets 2026-05-13 18:18:13 +10:00
Hiroku 6f614eb02e Merge branch 'implement_pokemon' into 'main'
Implement pokemon

See merge request cable-mc/cobblemon!2232
2026-05-13 08:11:53 +00:00
Paul 72b82858b3 Merge remote-tracking branch 'origin/main' into Habitat-Block-Implementation 2026-05-13 17:16:45 +10:00
Hiroku 361047c309 Merge branch 'Habitat-Block-Implementation' into 'main'
Habitat block implementation

See merge request cable-mc/cobblemon!2261
2026-05-13 06:58:56 +00:00
Paul 18f43f72ad Fixed multiplayer joins failing due to interactions with no cooldown field not loading defaults properly 2026-05-13 16:46:48 +10:00
Paul cbb409b057 Translatable lang for all the habitat editor fields 2026-05-13 16:07:18 +10:00
Paul f8cec09f70 Resolve some conflicts with habitat JSONs and made lang keys for all the to-be-implemented ones 2026-05-13 15:33:44 +10:00
Paul 5579975b46 Merge remote-tracking branch 'origin/main' into Habitat-Block-Implementation
# Conflicts:
#	common/src/main/resources/data/cobblemon/habitat_pools/abandoned_fortress.json
#	common/src/main/resources/data/cobblemon/habitat_pools/chorus_briar.json
#	common/src/main/resources/data/cobblemon/habitat_pools/crimson_fungal_dwelling.json
#	common/src/main/resources/data/cobblemon/habitat_pools/drifting_icebergs.json
#	common/src/main/resources/data/cobblemon/habitat_pools/quartz_spikes.json
#	common/src/main/resources/data/cobblemon/habitat_pools/skeletal_fall.json
#	common/src/main/resources/data/cobblemon/habitat_pools/strange_fungal_dwelling.json
#	common/src/main/resources/data/cobblemon/habitat_pools/volcanic_plume.json
#	common/src/main/resources/data/cobblemon/habitat_pools/warped_fungal_dwelling.json
2026-05-13 14:53:29 +10:00
CaptainLennie 5a57f41fbb Fixed snivy's back problems 2026-05-12 15:52:00 +02:00
CaptainLennie 1845faf9ad Updated samurott textures 2026-05-12 15:48:54 +02:00
Polymeta 0e93682457 Merge branch 'updated-spawns-and-drops' into 'main'
Scrapes for spawns and drops and habitat pools

See merge request cable-mc/cobblemon!2262
2026-05-12 09:06:32 +00:00
ewwww cef69f478d fixed alpha eyes for rattata, raticate, raichu, sandshrew, & sandslash
added locators for nidoran♀, nidorina, nidoqueen, nidoran♂ , nidorino, vulpix (both forms), ninetales (both forms), jigglypuff, wigglytuff, oddish, gloom, vileplume, paras, parasect, venonat, venomoth, diglett (both forms), dugtrio (both forms), meowth (all forms), persian (both forms), psyduck, golduck, mankey, primeape, and growlithe
2026-05-12 04:55:45 -04:00
Paul 5ea7bc9c48 Updated spawn scraper to do habitats too and fixed some drop scrapes 2026-05-12 18:15:22 +10:00
Paul bfc8ace83a Updated spawn scraper to use google doc and rescraped spawns and drops 2026-05-12 16:13:55 +10:00
Paul e0f93fd797 Added herdLevelRange property in herds to do what the levelRange used to, levelRange is now a clamping around un-offset spawn level 2026-05-12 16:13:40 +10:00
Slippy ba13414ada Changed the biome tags of quartz spikes and strange fungal dwellings 2026-05-12 00:36:02 -05:00
Slippy 66c63d54db added end stone to habitat block mimic list 2026-05-12 00:29:19 -05:00
Slippy 9e836bcc3b added habitat blocks to drifting icebergs habitat 2026-05-12 00:23:04 -05:00
Slippy 27fb1f6c66 Implementing Placeholder Habitat Block Jsons 2026-05-11 19:08:40 -05:00
Tyzillion 7f60885f92 I dum dum and did not account for breeze wind render type not allowing emissive. Made it so that if the layer has emissive enabled it uses the energy swirl render type instead. 2026-05-11 18:48:07 -04:00
Sterrezo cb7f6d7bba Updated TM Machine sounds (again), separated the starting sound with a loop 2026-05-11 20:15:54 +02:00
Polymeta a3498fe03b Merge branch 'small-fix' into 'main'
Remove Unused ClientDataSynchronizer

See merge request cable-mc/cobblemon!2240
2026-05-11 14:46:26 +00:00
Polymeta af4af3395c Merge branch 'two-sorting-tweaks' into 'main'
Two Sorting Tweaks

See merge request cable-mc/cobblemon!2259
2026-05-11 14:29:57 +00:00
ewwww 3ab07ecbca added cries to sandshrew + proper alpha eyes for sandslash 2026-05-11 05:26:17 -04:00
dhi_holo b96eccbf2f Changed Regional Delicacy sorting to match dex order 2026-05-11 18:08:46 +10:00
dhi_holo afa7426116 Update CobblemonItemGroups.kt
Fixed Type Gem Block order
2026-05-11 16:15:13 +10:00
ewwww a5ace52cb5 typo 2026-05-10 22:43:32 -04:00
Sterrezo 9f3940bb31 Volume rebalancing 2026-05-10 22:55:35 +02:00
ewwww 8a8c131a44 Primarina size fix 2026-05-10 16:39:59 -04:00
ewwww 8b0fba4201 SANDSHREW REMODEL (and locators) 2026-05-10 06:56:25 -04:00
Hiroku e1e04b19bf Merge branch 'fix-collecting-honey-from-saccharine-leaf-block-with-full-inventory' into 'main'
Refactor addItem() call to giveOrDropItemStack()

Closes #2009

See merge request cable-mc/cobblemon!2253
2026-05-10 10:23:19 +00:00
Hiroku a04beb1ce8 Merge branch 'fix-remove-mulch-from-berry-tree' into 'main'
Fix remove mulch from berry tree

Closes #2011

See merge request cable-mc/cobblemon!2258
2026-05-10 10:22:41 +00:00
ewwww bd9aea7c59 pikachu + raichu updated locators 2026-05-10 05:01:12 -04:00
Sterrezo 757e3a7ff7 Burn sound now stops when crafting TMs is done 2026-05-10 10:37:19 +02:00
Paul 66d03651f9 Added q.failed to move instruction action effects 2026-05-10 13:23:10 +10:00
Faintful 3c1577d58f Add changelog 2026-05-10 02:06:45 +02:00
Faintful dd7d562228 Add changelog 2026-05-10 01:56:52 +02:00
Faintful a01ac32e54 Fix setMulch() not being called in useWithoutItem() 2026-05-10 01:38:43 +02:00
Sterrezo 5c0ae854bf Fixed TM inserting sound playing twice, more volume tweaks 2026-05-09 19:22:02 +02:00
Sterrezo 55c189b749 TM Craft now plays from the block + volume tweaks to the TM Machine 2026-05-09 19:11:49 +02:00
Gertimoshka b5b81fa1aa Implemented the TMM sounds 2026-05-09 19:19:54 +03:00
Sterrezo eda58859ce Fix empty tm machine sounds 2026-05-09 17:33:58 +02:00
Sterrezo 3888a48cb0 Tweaks to block sound positions and the /pc command 2026-05-09 16:20:52 +02:00
ewwww c9b609a3f8 rattata + raticate locators (idgaf if raticate looks a little wonky for now IT'S GETTING REMODELED ANYWAY) 2026-05-09 06:55:57 -04:00
Sterrezo 70fb7c87ad Wip implementation of TMM sounds 2026-05-09 12:45:48 +02:00
Sterrezo a24189b816 Volume tweaks to display cases and evolution stone blocks 2026-05-09 12:05:42 +02:00
Gertimoshka c0d80634a4 Regional Food are now attempted to be consumed first, before applying use on, shifting will skip the consuming 2026-05-08 13:37:52 +03:00
Gertimoshka b52e4de85c Pokerod Bobber will promtly self destruct if the bait of used rod is changed 2026-05-08 13:26:21 +03:00
Gertimoshka 7d37a98a58 Aprijuice will be usable on pokemon with Max Ride stats if they decrease stat, as well as checking for stat to not decrease below allowed 2026-05-08 11:27:45 +03:00
Hiroku a424796aa8 Merge branch 'fix/particle-material-batching' into 'main'
Fix particle culling and shader wiring for per-material batching

See merge request cable-mc/cobblemon!2254
2026-05-08 06:12:28 +00:00
Faintful 8518be0d44 Add conditional giveOrDropItemStack() call for returning empty bottles 2026-05-07 20:35:41 +02:00
terrainwax 24760270bd Fix particle culling and shader wiring for per-material batching 2026-05-07 20:13:36 +02:00
Faintful 19f9490274 Refactor addItem() call to giveOrDropItemStack() 2026-05-07 18:56:02 +02:00
ewwww 93a94f04a1 a portion of the gen 1 locator fixes
bulbasaur, ivysaur, venusaur, charmander, charmeleon, charizard, squirtle, wartortle, blastoise, caterpie, metapod, butterfree, weedle, kakuna, beedrill, pidgey, pidgeotto, pidgeot, spearow, fearow, & ekans
2026-05-07 05:44:20 -04:00
CaptainLennie 97eac48cd7 Alpha locator'd emolga and updated its model 2026-05-06 22:39:01 +02:00
CaptainLennie 2c31f002b2 Unova alpha fixes part 5 2026-05-06 22:16:33 +02:00
Polymeta 4f0eb16ab0 Merge branch 'interact-wheel-qol' into 'main'
Pressing the inventory key now closes the interact wheel gui

See merge request cable-mc/cobblemon!2247
2026-05-06 06:20:00 +00:00
Polymeta 5004b61967 Merge branch 'Structure-Fixes-Part-4' into 'main'
Structure fixes part 4

See merge request cable-mc/cobblemon!2251
2026-05-06 06:16:34 +00:00
MyslippyandPolymeta cf137efa02 Structure fixes part 4 2026-05-06 06:16:34 +00:00
Breezyasha 862c0bda89 update changelog 2026-05-06 00:17:19 -04:00
Gertimoshka 868874b06d Stop some sounds from causing lag from constantl being created properly, then being removed on the same tick later 2026-05-05 13:53:23 +03:00
Sterrezo 068e4a1c92 Pressing the inventory key now closes the interact wheel gui 2026-05-05 08:55:26 +02:00
Breezyasha 267f2aa5a8 Merge branch cobblemon:main into click-to-cry 2026-05-05 03:31:40 +00:00
Polymeta 333d8fa732 Merge branch 'sterr/sound' into 'main'
Couple of sound tweaks

See merge request cable-mc/cobblemon!2245
2026-05-04 19:43:06 +00:00
Sterrezo e8d4e406d5 Update CHANGELOG.md 2026-05-04 18:39:53 +02:00
Sterrezo 9e5c296309 More sound tweaks and fixes to type gems 2026-05-04 18:38:46 +02:00
Sterrezo 69dc89b04f Replaced the apricorn harvesting sound with a new placeholder 2026-05-04 18:14:35 +02:00
Sterrezo dd31fb1a51 Lowered the volume of berry harvesting sound 2026-05-04 17:54:39 +02:00
Sterrezo 9b3a729b3c Type gem block and cluster sound update 2026-05-04 17:53:46 +02:00
Polymeta ed732429b9 Merge branch 'pokedex-firmware-update' into 'main'
Fix to 1.8 breaking older version dex data and failing to load it

See merge request cable-mc/cobblemon!2244
2026-05-04 09:47:45 +00:00
Gertimoshka 8d27394665 Fix to 1.8 breaking older version dex data and failing to load it 2026-05-04 11:58:30 +03:00
Hiroku 943c421b1d Merge branch 'porygon-monitor-fix' into 'main'
Porygon Bug Fixes

See merge request cable-mc/cobblemon!2212
2026-05-04 01:35:56 +00:00
MeAlam1andPolymeta cc9541c146 make neoforge generate normal worlds without crashing agin 2026-05-04 00:36:31 +01:00
CeeJay 538f72c474 Merge branch 'main' into porygon-monitor-fix 2026-05-03 19:34:31 -04:00
CaptainLennie c72ab20cfa Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-03 22:26:42 +02:00
CaptainLennie 3cfc915b1a Unova alpha fixes part 4 2026-05-03 22:15:35 +02:00
Jerzykv b8541486d4 Implemented Smoliv line remodels 2026-05-03 21:16:42 +02:00
Jerzykv c69b38454e Fixed tiny error in Talonflame's animations 2026-05-03 20:59:11 +02:00
CaptainLennie 0e5787b8ab Unova alpha fixes part 3 2026-05-03 20:54:17 +02:00
plastered_crab 082be6f2d0 - Show SEEN pokemon in dex filters for Rideable and Learnable TM Moves
- Fix Rideable dex filter to show all rideable pokemon if SEEN or OWNED
2026-05-03 11:49:25 -07:00
plastered_crab 398f14efd5 Reveal drops and move info on Seen / Scanned Pokemon too 2026-05-03 11:15:43 -07:00
Polymeta 4ba33c2a80 neoforge can boot into singleplayer again
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-05-03 17:37:09 +01:00
CaptainLennie e8ace8ba05 Unova alpha fixes part 2 2026-05-03 13:05:35 +02:00
CaptainLennie 1a78625111 Unova Model & locator fixes part 1 2026-05-03 11:55:24 +02:00
CaptainLennie 596c2b95b4 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-03 11:07:19 +02:00
CaptainLennie 38648111a6 Updated torchic locators 2026-05-03 11:07:08 +02:00
JadedArts 149b8f27e6 Draco Meteor is no longer backwards 2026-05-02 18:35:14 -05:00
Hiroku dc78e40941 Merge branch 'fix/disable-culling-in-snowstorm' into 'main'
fix: disable culling for SnowstormParticle

See merge request cable-mc/cobblemon!2239
2026-05-02 23:24:11 +00:00
Polymeta ca7e2054fe Merge branch '1.8-qa-fixes-#2' into 'main'
1.8 qa fixes #2

Closes #2

See merge request cable-mc/cobblemon!2241
2026-05-02 20:43:11 +00:00
Polymeta 14ea73332f Merge branch 'Structure-Fixes-Part-3' into 'main'
Round 3 of structure fixes. Rates adjusted for fossil structures and nether...

See merge request cable-mc/cobblemon!2242
2026-05-02 20:42:07 +00:00
ewwww 91433d7e13 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-02 15:49:06 -04:00
ewwww 85aadbabe9 i need 2 leave this somewhere , raichu alpha eyes texture 2026-05-02 15:48:44 -04:00
Slippy 8fed14e706 Round 3 of structure fixes. Rates adjusted for fossil structures and nether habitats. Fixed holes in deep roots. Fixed one strange fungal dwelling getting cut off by world generation due to size. Changed the step at which preserved skeleton and submerged spike generates so that they actually generate. I swear to god, if the enhydro agate doesnt generate this time, I don't know what else to do. Changed the biome tag of fungal dwelling habitat. 2026-05-02 12:31:56 -05:00
RedGenesectNinja e64c1487cf Restored Ninjask's ability to do barrel rolls 2026-05-02 00:58:49 -05:00
dhi_holo 2c16e5c20d Relic Coin Sack Map Colour 2026-05-02 11:26:19 +10:00
dhi_holo 21505bc5de Update CobblemonItemGroups.kt
Added some items to some vanilla tabs to hint at functions to player, and make some items easier to find
2026-05-02 10:52:22 +10:00
dhi_holo a365aca9ca Cluster Map Colours
We didn't do this, Minecraft does, correcting behaviour now
2026-05-02 10:13:27 +10:00
dhi_holo 19b70e1fbb Colour Tweaks
Tweaked some colours to feel a bit closer. Not perfect, but probably as close to it as we can get with the existing palettes
2026-05-02 10:04:01 +10:00
Plastered_Crab e1ec69f4cf Give Type Gem blocks unique map colors 2026-05-01 15:35:21 -07:00
Plastered_Crab c23e506570 Override culling distance of HabitatBlock so it acts more like terrain blocks. 2026-05-01 15:09:34 -07:00
Plastered_Crab 28a4805cd3 Delete unneeded file 2026-05-01 14:54:08 -07:00
Plastered_Crab 8d3a895591 Merge branch 'main' into 1.8-qa-fixes-#2
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/block/entity/DiscShelfBlockEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
2026-05-01 14:31:06 -07:00
Plastered_Crab e8089d6a5d Clean up and optimize some TM Shelf logic 2026-05-01 14:20:41 -07:00
Plastered_Crab 88988c21f1 TM Shelf rendering and rotation fixes 2026-05-01 13:17:14 -07:00
Plastered_Crab cf4409e61d Switch Sound Source of Disk Rack to be RECORDS instead of BLOCKS 2026-05-01 12:24:23 -07:00
Plastered_Crab 516343406c Stop false exception when spawning a pokemon 2026-05-01 11:54:16 -07:00
Plastered_Crab 5ecbf33a6b Fix corrupted player data crash when mount pokemon dies 2026-05-01 11:15:15 -07:00
JadedArts 4d866f6207 More Draco Meteor Fixes
Tweaked ring color gradient
Made crater rocks affected by environment lighting
Fixed meteors playing even on miss
2026-05-01 12:21:56 -05:00
MeAlam1 e81418836e Remove Unused ClientDataSynchronizer 2026-05-01 17:44:57 +02:00
terrainwax 03786a9845 fix: stick to bedrock spec about material 2026-05-01 05:40:15 +02:00
terrainwax d199e372ca fix: disable culling for SnowstormParticle ( all particle ar not always facing camera so we need to render theirs back if we see fps drop for large number of particle we should consider adding a if to camera mod that are not facing the camera ) 2026-05-01 05:19:00 +02:00
Polymeta 8022bfefc8 Merge branch 'command-anim' into 'main'
[Enhancement] Let NPCBattleActors trigger the "Command" animation in battle

See merge request cable-mc/cobblemon!2152
2026-04-30 23:39:25 +00:00
Konoha DandPolymeta eb82d6d6a9 [Enhancement] Let NPCBattleActors trigger the "Command" animation in battle 2026-04-30 23:39:25 +00:00
Polymeta 5be8fdd03d close #1997
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-05-01 00:00:04 +01:00
CaptainLennie 3d779e637b Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-05-01 00:59:52 +02:00
CaptainLennie bd0ad89971 Implemented shedinja, skwovet & greedent animations 2026-05-01 00:59:37 +02:00
Polymeta 0a0532c8cc Merge branch 'trade_friendship' into 'main'
Caching trade friendships to player data

Closes #1898

See merge request cable-mc/cobblemon!2119
2026-04-30 22:55:22 +00:00
Polymeta 586ced7bfb Merge branch 'main' into 'implement_pokemon'
# Conflicts:
#   common/src/main/resources/data/cobblemon/recipe/metal_alloy.json
2026-04-30 22:43:58 +00:00
aeroeng15 e1551ba70b MR Updates 2026-04-30 15:41:28 -07:00
Polymeta 9e05ef3e8c Merge branch 'alpha-bait-changes' into 'main'
Size Modifying Bait effect

See merge request cable-mc/cobblemon!2237
2026-04-30 21:55:18 +00:00
Charzard4261andPolymeta ea1608c8d2 Size Modifying Bait effect 2026-04-30 21:55:18 +00:00
Sterrezo 218f2c67d3 The real draco meteor was friends we made along the way (more sound tweaks) 2026-04-30 20:14:11 +02:00
Polymeta 045ddb30ca Merge branch 'Structure-Fixes-Round-2' into 'main'
Series 2 of 1.8 Structure Fixes

See merge request cable-mc/cobblemon!2238
2026-04-30 17:40:19 +00:00
Slippy bf3e26874f Never updated the fossil structure nbts. Whoops 2026-04-30 01:57:35 -05:00
Slippy bfbdadb519 Make surface habitats sliiiightly more common 2026-04-30 01:52:59 -05:00
Slippy 6b2bd64eec Maybe this will get enhydro agates to generate? 2026-04-30 01:46:10 -05:00
Slippy 59e300582c Chorus Briars way too common 2026-04-30 01:37:28 -05:00
Slippy dd75b898a6 skeletal falls a little too rare. brought it up a bit 2026-04-30 01:35:17 -05:00
Slippy b3bd2e26cd Rates for some habitats ended up being way too low 2026-04-30 01:32:53 -05:00
Slippy 475f8de2e6 Increased odds of a second piece for the fae_mounds 2026-04-30 00:49:32 -05:00
Slippy 096785d7ec added grass, water and fire pledge to jungle temple, nether fortresse and shipwreck chest loot. 2026-04-30 00:46:24 -05:00
Slippy c09c85ffd8 Fixed deep crypt ruins not summoning the warden 2026-04-30 00:39:22 -05:00
Slippy 9f5acfbf62 Adjusted generation rates for all ruin and habitat features and upped the chance to find the enhydro agate. 2026-04-30 00:25:50 -05:00
Slippy d0ddf3bb11 Fixed root nurseries cutting off parts of vanilla trees when generating 2026-04-30 00:18:41 -05:00
Slippy 50624a4e34 Fixed deep sea placeholder nests being replaced with air, not water. Fixed biome tags for natural lightning rod and sandpit clearing habitats. 2026-04-30 00:14:13 -05:00
Slippy 19733e53c9 Fixed bones for skeletal_fall habitats facing the wrong ways 2026-04-30 00:01:37 -05:00
Slippy 277e72912c fixed natural terrain generating in what's supposed to be the air pockets of the structures of fungal dwellings 2026-04-29 23:53:21 -05:00
Breezyasha 59b0397ea4 Merge branch cobblemon:main into click-to-cry 2026-04-30 04:22:08 +00:00
Breezyasha aadb78149c changelog 2026-04-30 00:19:51 -04:00
Slippy 23c80bb82c Fixed shipwreck coves not having structure voids and hopefully fixed the habitats tag 2026-04-29 22:56:10 -05:00
Test 143f9dba3c re-add tm center overlay 2026-04-29 23:45:05 -04:00
aeroeng15 2fb019ce8a Check the right field! 2026-04-29 19:45:46 -07:00
aeroeng15 487b59f7b5 Fix compile issues 2026-04-29 19:23:21 -07:00
aeroeng15 b06c2253ae MR Updates 2026-04-29 19:14:33 -07:00
CaptainLennie 3305874dd4 Implemented sewaddle & swadloon animations 2026-04-30 00:31:32 +02:00
CaptainLennie 2f688291dc Implemented leavanny animations 2026-04-29 23:20:50 +02:00
Polymeta 457c2ab84d Merge branch 'unlag-your-tmm-slots' into 'main'
Fix occasionally slots 'lagging' and not reading your input properly

See merge request cable-mc/cobblemon!2236
2026-04-29 20:36:15 +00:00
CaptainLennie 1f099b231c Fixed talonflame line walk speeds & flygon flight sounds 2026-04-29 21:48:35 +02:00
JadedArts c1ccc718cc Fixes to particles
Renamed explosion_floorscorch.png and explosion_floorscorch_big.png to floorscorch.png and floorscorch_big.png and moved to the generic folder under a new folder "scorch" and updated Explosion and Draco Meteor particles accordingly.

Fixed issue in flamethrower_actor that caused drag to be able to be infinite.
2026-04-29 12:22:40 -05:00
Gertimoshka 972d09e920 Fix occasionally slots 'lagging' and not reading your input properly 2026-04-29 19:30:13 +03:00
Jade 74df81006b Slightly reduced delay at the start of Draco Meteor 2026-04-29 15:23:27 +00:00
Sterrezo e7a87e299c First draft of draco meteor sound implementation 2026-04-29 14:25:56 +02:00
Polymeta c0bef49e2c [skip-ci] changelog
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-29 09:29:16 +01:00
Polymeta 44c9ca3c6b Merge branch 'advancing-advancements-returns-in-1.8' into 'main'
Advancing Advancements into 1.8

See merge request cable-mc/cobblemon!2223
2026-04-29 08:27:14 +00:00
dhi_holo d02fe990f6 Typos 2026-04-29 16:51:12 +10:00
dhi_holo 53e133ab94 Update use_mint.json
Fixed requirements
2026-04-29 16:35:33 +10:00
ewwww c688e9f80c Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-04-28 23:02:47 -04:00
ewwww ac1897b41a updated stat increase/decrease particles and added alpha boost particles 2026-04-28 23:02:01 -04:00
ewwww 40e8f4efa5 added eye locators to Nidoking's model 2026-04-28 23:01:34 -04:00
ewwww e00165aafb added animation for Draco Meteor 2026-04-28 23:01:14 -04:00
dhi_holo 7d7c4fa3e2 Final Root is Named 2026-04-29 12:12:47 +10:00
dhi_holo 3c5c4b9cd0 Audio Sequencer
New Tutorial Advancement Time!
2026-04-29 12:07:29 +10:00
aeroeng15 a83546a1da Changelog shenanigans 2026-04-28 18:43:20 -07:00
aeroeng15 af6405b078 Revert "Changelog shenanigans"
This reverts commit ae5021a851.
2026-04-28 18:42:58 -07:00
aeroeng15 ae5021a851 Changelog shenanigans 2026-04-28 18:41:22 -07:00
aeroeng15 ebbe4bb0e1 Re-null friendship after restoration 2026-04-28 18:39:42 -07:00
aeroeng15 ebe93f531c Restore file 2026-04-28 18:39:42 -07:00
aeroeng15 e489bcde07 Change from player-based to pokemon-based storage 2026-04-28 18:39:42 -07:00
aeroeng15 64a3f2dd28 Fix after rebase 2026-04-28 18:39:42 -07:00
aeroeng15 ba15ca3d02 Changelog 2026-04-28 18:39:41 -07:00
aeroeng15 4b79d51f7b Cache management while players are offline 2026-04-28 18:39:41 -07:00
aeroeng15 4c68b0f837 Caching trade friendships to player data 2026-04-28 18:39:41 -07:00
Breezyasha b2015c9cd8 Play pokemon cry in summary screen 2026-04-28 21:26:08 -04:00
dhi_holo eaf0d7ec80 It's Not That Big A Commit-mint
New Mint advancement to be a better tutorial and a more fitting role
2026-04-29 10:09:36 +10:00
GenotypeXD d0b796f777 Changelog Fixes 2026-04-28 19:07:25 -05:00
GenotypeXD 83488ec728 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon
# Conflicts:
#	common/src/main/resources/assets/cobblemon/textures/pokemon/0124_jynx/jynx_alpha.png
#	common/src/main/resources/assets/cobblemon/textures/pokemon/0729_brionne/brionne_alpha.png
2026-04-28 19:02:01 -05:00
GenotypeXD b3d387d8da Implemented Jynx Remodel, Popplio line Remodel, and Fletchling line animations 2026-04-28 19:00:42 -05:00
dhi_holo d9770b17f0 Potion Advancement Improved 2026-04-29 08:58:30 +10:00
dhi_holo b9b4b1c2e7 Merge branch 'main' into advancing-advancements-returns-in-1.8 2026-04-29 08:03:14 +10:00
dhi_holo 13f33f935c Update en_us.json 2026-04-29 08:03:03 +10:00
Apion 3fbcfe842d Merge branch '1.8-structure-fixes' into 'main'
Remoeved the old habitat test structure, added tags for habitats and the new...

See merge request cable-mc/cobblemon!2234
2026-04-28 21:57:10 +00:00
Slippy d5503f7fa9 rocky tidepools should generate now 2026-04-28 16:05:58 -05:00
Slippy fd3e599636 Remoeved the old habitat test structure, added tags for habitats and the new structures and the new shipwreck cove. One freshwater pond's weight was set to 100 by accident. set to 1 now. 2026-04-28 15:55:43 -05:00
Slippy a70a258e64 Upped the rates of some pieces generating in shaded rocks and snowy burrows 2026-04-28 15:36:29 -05:00
Slippy 8121cb4e87 Fixed volcanic plumes, skeletal falls and strange fungal dwellings not generating 2026-04-28 15:30:41 -05:00
Slippy a1ef50a8ec Hopefully this makes rocky outcrop, parched pog and lush pog generate 2026-04-28 15:21:18 -05:00
Polymeta c9c5488be7 Merge branch 'type-gem-feature-gaslighting-max-fix' into 'main'
Fix to Type Gem Feature having a chance to cause an infinite loop during generation, stalling world gen

See merge request cable-mc/cobblemon!2233
2026-04-28 18:46:06 +00:00
Slippy f356dd98b2 Hopefully this fixes the chests in the shipwreck coves 2026-04-28 13:24:32 -05:00
Gertimoshka ce7e465d99 Fix Type Gem Feature Generation Gaslightight causing an infinite loop, stalling world gen 2026-04-28 20:06:00 +03:00
CaptainLennie 902c6751c5 Added milotic ride animations 2026-04-28 15:22:22 +02:00
Polymeta 463d341965 [skip-ci] changelog
Signed-off-by: Polymeta <40173118+Polymeta@users.noreply.github.com>
2026-04-28 13:19:09 +01:00
CaptainLennie 4088f144e4 Fixed mantine's ride sounds 2026-04-28 13:57:09 +02:00
CaptainLennie 3e592b21f1 Fixed honchkrow's ride sounds & added new wing flap sounds 2026-04-28 13:48:08 +02:00
CaptainLennie b1186c8033 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-04-28 13:21:52 +02:00
CaptainLennie d038f63024 Fixed crobat's ride sounds & added new wing flap sounds 2026-04-28 13:21:26 +02:00
dhi_holo b5a0866b31 Merge branch 'main' into advancing-advancements-returns-in-1.8 2026-04-28 21:02:53 +10:00
dhi_holo 6b1d17ea5d namespace correction 2026-04-28 21:00:24 +10:00
Polymeta 49fe33f9db Merge branch 'sterr/log-cleaning' into 'main'
Return of the log

See merge request cable-mc/cobblemon!2231
2026-04-28 10:57:56 +00:00
Sterrezo 7a3088eb59 Unbroke everything and fixed Alolan Sandslash as well 2026-04-28 12:30:43 +02:00
Polymeta 32ff4557a7 Merge branch 'main-crab-1.8-fixes' into 'main'
1.8 fixes from QA Doc

See merge request cable-mc/cobblemon!2230
2026-04-28 10:27:30 +00:00
CaptainLennie de51d2b1b2 Updated drampa's poser 2026-04-28 12:25:49 +02:00
CaptainLennie 3c3aa3d4a3 Updated aerodactyl locators & changelog 2026-04-28 12:19:51 +02:00
Sterrezo 50e4829b79 Fixed Sandslash's missing cry crashing the game 2026-04-28 12:17:46 +02:00
Sterrezo e4b3136bc9 Removed a nonexistent sound from a sound event 2026-04-28 12:06:22 +02:00
Sterrezo 0815a93ee0 Merge branch 'main' into sterr/log-cleaning 2026-04-28 12:03:15 +02:00
ewwww ebdd06a4a9 added hisui bias bergmite model 2026-04-28 05:56:02 -04:00
ewwww 82fd4bae08 goomy, dartrix, and rowlet hisui bias updates 2026-04-28 05:17:58 -04:00
ewwww 277a7dc32b added mime jr galar bias 2026-04-28 04:36:57 -04:00
ewwww f00d578f64 updated dewott hisui bias 2026-04-28 04:36:40 -04:00
ewwww ea9fba72ec added metal alloy recipe 2026-04-28 04:36:23 -04:00
ewwww 4654c3c2b8 updated oshawott hisui bias 2026-04-28 04:36:12 -04:00
ewwww b4496b30a9 updated petilil hisui bias 2026-04-28 04:36:01 -04:00
ewwww 431662b237 updated exeggcute alola bias 2026-04-28 04:35:42 -04:00
ewwww 1299214d61 updated koffing galar bias 2026-04-28 04:35:26 -04:00
ewwww c5c2c3a059 updated quilava hisui bias 2026-04-28 04:35:05 -04:00
ewwww 64b7970cb5 updated cyndaquil hisui bias 2026-04-28 04:34:51 -04:00
ewwww ce6c777c4e updated cubone alola bias 2026-04-28 04:34:19 -04:00
ewwww 284b032dd8 updated pichu alola bias 2026-04-28 04:33:42 -04:00
ewwww 23d70ab1fe updated pikachu alola bias 2026-04-28 04:33:07 -04:00
Slippy 9414cc096a Several Structure fixes 2026-04-28 02:48:29 -05:00
dhi_holo 7f7774ce90 Advancement Cleanup 2026-04-28 17:14:41 +10:00
dhi_holo 6fde014c67 Advancement Refactor Time!
Drastic reorganisation of the Advancements tab and folder, which will remove all progress on advancements obtained previously, as well as removing some advancements deemed unneeded.
2026-04-28 16:31:31 +10:00
ewwww 3340239440 took garchomp to therapy so it no longer sleep t-poses 2026-04-27 22:27:29 -04:00
ewwww e513d52c3d fixed nidoking's walk speed 2026-04-27 21:59:26 -04:00
dhi_holo ba54401c49 Update root_geological.json 2026-04-28 10:56:26 +10:00
dhi_holo 2d4723d981 Disc Shelf Block Texture Cleanup
Moved the textures out of the main block folder for consistency
2026-04-28 10:40:10 +10:00
dhi_holo d91292cfb6 Update CHANGELOG.md
Moved data from Changelog-1.8 to Changelog
2026-04-28 09:28:34 +10:00
dhi_holo 7156c99b92 Merge branch 'main' into advancing-advancements-returns-in-1.8 2026-04-28 09:21:08 +10:00
dhi_holo 7320e3b9cc Update en_us.json 2026-04-28 09:19:59 +10:00
dhi_holo db9634c254 Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-27 15:27:31 +10:00
dhi_holo b06628e6ba Geology Root 2026-04-27 15:24:31 +10:00
dhi_holo eccba938c7 Recipe Advancement Sorting Tweak 2026-04-27 14:46:22 +10:00
dhi_holo d6f71abb51 Advancement Sorting/Start of Cleanup 2026-04-27 14:29:19 +10:00
dhi_holo ad7bb84d5c Corrected Requirement 2026-04-27 11:56:14 +10:00
dhi_holo 69eb396229 Fixed my mistake 2026-04-27 11:35:21 +10:00
dhi_holo 06abdaf683 Merge remote-tracking branch 'origin/feat/add_trigger' into advancing-advancements-returns-in-1.8 2026-04-27 11:16:40 +10:00
dhi_holo 79e0c2f1bf Advancement Shuffle - Potion 2026-04-27 11:15:20 +10:00
dhi_holo dbee413af5 Finalized Cleanup
New Advancement Names Completed
2026-04-27 09:52:25 +10:00
dhi_holo 21a1d839ae Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-27 09:40:24 +10:00
dhi_holo 353f7f6d33 Update en_us.json
New Advancement Names Completed
2026-04-27 09:36:34 +10:00
dhi_holo 35b28075e4 Merge branch '1.8-gert-fixes' into 'advancing-advancements-returns-in-1.8'
Additional Gem Fixes and TM Shelf refactoring to Disc Shelf

See merge request cable-mc/cobblemon!2222
2026-04-27 07:41:58 +10:00
CeeJay 9660e64dff Merge branch '1.8.0' into porygon-monitor-fix 2026-04-26 13:53:21 -04:00
CeeJay f992a9cf0f move sounds to client side, and make them Cancellable 2026-04-26 03:32:34 -04:00
Gertimoshka 54112f02b5 Fixed Pickblock and Silktouch Gem Clusters not storing data 2026-04-26 09:38:29 +03:00
Gertimoshka e98fc55dcb What? 2026-04-26 09:29:03 +03:00
Gertimoshka 2f907988fb Gem Clusters will have predefined stunted on the item to stop annoying stacking 2026-04-26 09:09:49 +03:00
dhi_holo c81494bec2 Update CHANGELOG-1.8.md 2026-04-26 15:56:02 +10:00
dhi_holo cd965961ae Update obtain_ancient_tm.json
Thank you Berber, for your diligent work cataloguing what TMs are needed for this
2026-04-26 15:53:14 +10:00
Gertimoshka 52238abe35 Finalize Refactor, Correct Gem placement Sound 2026-04-26 08:50:15 +03:00
dhi_holo 7594582520 Advancements Advanced
Need a list of the Ancient TMs and to merge with !2221 to complete and be ready to merge
2026-04-26 15:19:37 +10:00
Gertimoshka a240e4e48c Merge branch 'advancing-advancements-returns-in-1.8' into 1.8-gert-fixes 2026-04-26 08:08:35 +03:00
Gertimoshka 4dc279b664 Disc Shelf dont consume item on creative and TM Shelf refactor to Disc Shelf 2026-04-26 08:00:31 +03:00
dhi_holo b3e59b5151 Merge branch 'advancing-advancements-returns-in-1.8' of https://gitlab.com/cable-mc/cobblemon into advancing-advancements-returns-in-1.8 2026-04-26 14:18:29 +10:00
dhi_holo abfaec1d05 Advancement Advances 2026-04-26 14:17:46 +10:00
terrainwax e503ddbc69 add trigger for type gem + handler 2026-04-26 06:16:53 +02:00
dhi_holo 5c5c236d1b Corrected Reference 2026-04-26 12:55:51 +10:00
dhi_holo ee4e9dd250 Fixed Missing Loot Table 2026-04-26 12:44:26 +10:00
dhi_holo 27cb0dc449 Deepslate Crystal Core Model Fix 2026-04-26 12:39:53 +10:00
dhi_holo 5781c18868 Matched to Refactor
Updated to match the Type Gem Refactor
2026-04-26 12:10:44 +10:00
dhi_holo b43e5e180e Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-26 12:02:57 +10:00
dhi_holo 913d11cccc Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-26 11:28:32 +10:00
Gertimoshka 3f6a4bbae4 Gem items are no longer "block items", and now you can clone gem clusters with appropriate stage and they show in creative tab 2026-04-25 16:34:05 +03:00
dhi_holo 6100252b3e Update CobblemonItemGroups.kt
Added Habitat Block to Operator Items
2026-04-25 20:27:08 +10:00
dhi_holo 27be7c6eaf Budding Visual Correction
We Forgot The Display Settings!
2026-04-25 19:45:18 +10:00
dhi_holo 18b3cf4236 Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-25 18:35:33 +10:00
ewwww edd4d18150 corrected hydrapple's german name 2026-04-25 03:30:41 -04:00
dhi_holo 0e328b2166 Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-25 10:59:46 +10:00
dhi_holo 8f16e236e7 Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-25 07:45:04 +10:00
Sterrezo 88261125c0 Update CHANGELOG-1.8.md 2026-04-24 03:36:53 +02:00
Sterrezo eb528e6534 Fixed redundant ride loop sounds causing log errors
They're probably still redundant, but I don't know for sure, so I'm keeping them here but with correct sound events
2026-04-24 03:35:52 +02:00
Sterrezo 2982ef1c30 Restored Galarian Ponyta's cry 2026-04-24 03:24:02 +02:00
Sterrezo 813c949216 Removed licenses to prevent log spam 2026-04-24 03:21:51 +02:00
dhi_holo f66a94649b Revert Accidental Texture Backports
Mismatch of repos and running off autobrain
2026-04-23 23:21:55 +10:00
dhi_holo c495e967af Revert "Mipmap Fixes"
This reverts commit 73c9504d63.
2026-04-23 23:10:10 +10:00
dhi_holo 745bae1451 "Does the man want his appy slices??"
Saccharine Leaves can now drop the Applin Apples
2026-04-23 22:18:16 +10:00
dhi_holo 5fabc1bfaf Advancements Partially Named 2026-04-23 21:54:54 +10:00
dhi_holo 3aa6969fba Tag Growth
Increased the tagging to include new blocks
2026-04-23 21:44:33 +10:00
dhi_holo 73c9504d63 Mipmap Fixes 2026-04-23 20:30:47 +10:00
dhi_holo 63c352e5ca TYPOS 2026-04-23 19:51:49 +10:00
dhi_holo 0f9df28a82 Fixed Some File Names 2026-04-23 19:33:07 +10:00
dhi_holo 303ef76bd0 Texture Updates
Lots of them. Holy Shit There's A Lot, Actually
2026-04-23 19:08:16 +10:00
dhi_holo 9e27c1ec85 Update metal_alloy.json
grr underscores
2026-04-23 16:25:15 +10:00
dhi_holo b02edb7f78 Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-23 16:13:04 +10:00
dhi_holo 04b0915290 Advancement Icon Files
Adds an Advancement Icon
2026-04-23 16:12:48 +10:00
dhi_holo c26bd59c21 Update metal_alloy.json
Typo fix
2026-04-23 15:14:39 +10:00
dhi_holo f7d2776f7d Merge branch '1.8.0' into advancing-advancements-returns-in-1.8 2026-04-23 14:58:22 +10:00
dhi_holo 18ad6006ca Metal Alloy
Now with a Temporary Recipe!
2026-04-23 14:57:30 +10:00
dhi_holo 46ba44545c QADoc 24a
Evolutionary Item Drops, plus Changelog for QA Doc 24
2026-04-23 14:52:21 +10:00
dhi_holo 89a8a702c1 QADoc 24b
Added the 30 brushings, will changelog alongside 24a

Also fixed a typo
2026-04-23 13:10:14 +10:00
dhi_holo fc438520ba QADoc 25 and 26 2026-04-23 11:47:23 +10:00
dhi_holo 00836d8e51 New Advancements, Piece 1 2026-04-23 11:41:14 +10:00
dhi_holo 8a06113b5e Attempt at PlantTypeGem Advancement Criteria
Issue for more information: #2002
In theory, I think this works, but I don't understand the Advancement Handler enough to finish this into a functional advancement trigger
2026-04-22 19:01:12 +10:00
dhi_holo 77397929cb TM Shelf Missing Data
Added in all missing data files related to the TM Shelf, and added Damaged Monitor and TM Machine to the Machines tags
2026-04-22 18:06:47 +10:00
dhi_holo 402973f814 Update CobblemonItemGroups.kt
Fixed order inconsistency
Added Missed Blocks
2026-04-22 17:41:40 +10:00
dhi_holo 89d5660a5f Update relic_coin_pouch_case.json
Fixed Missing Texture Error
2026-04-22 14:42:05 +10:00
CeeJay 8b5bc78b5a Merge branch '1.8.0' into porygon-monitor-fix 2026-04-21 10:59:47 -04:00
CeeJay 1cfa1ce9fd new glitching sound for Dubious Disc in monitor 2026-04-21 02:23:33 -04:00
Sterrezo 4e821962e3 Added a looping sound for a dubious disc being used 2026-04-16 11:20:55 +02:00
CeeJay 962720a12c fix issues with breaking monitor during porygon process 2026-04-16 02:41:59 -04:00
Watuneni iteiteandWeblate a1b6bd79d2 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-04-11 21:33:38 +00:00
Watuneni iteiteandWeblate 83eb483f0b Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-03-26 06:45:59 +00:00
Watuneni iteiteandWeblate d8498d7a37 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-03-25 11:05:22 +00:00
Watuneni iteiteandWeblate 3693e7eec0 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-03-25 05:21:29 +00:00
Watuneni iteiteandWeblate 60c9f5d054 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-03-24 20:51:43 +00:00
washashu0905andWeblate e1541a3512 Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 98.2% (8898 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2026-03-22 01:23:31 +00:00
washashu0905andWeblate 1c1c3f51bb Translated using Weblate (Chinese (Traditional Han script))
Currently translated at 97.0% (8792 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hant/
2026-03-20 20:39:49 +00:00
阿鱼andWeblate 500f1f53a4 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-03-15 22:11:24 +00:00
Watuneni iteiteandWeblate 9ed10bb217 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-03-15 03:33:00 +00:00
Watuneni iteiteandWeblate 2f6d4313e7 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-03-13 18:46:43 +00:00
阿鱼andWeblate d640a04fa6 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-03-10 18:05:39 +00:00
geumuandWeblate 7749f346b5 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-03-06 16:37:11 +00:00
wajosalo21xandWeblate a29bc68663 Translated using Weblate (Spanish (Ecuador))
Currently translated at 98.3% (8908 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es_EC/
2026-03-03 21:07:51 +00:00
阿鱼andWeblate 1358380905 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-03-03 21:07:51 +00:00
wajosalo21xandWeblate 2679885fb4 Translated using Weblate (Spanish (Ecuador))
Currently translated at 98.3% (8908 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es_EC/
2026-02-28 19:45:51 +00:00
阿鱼andWeblate 278c5d8e81 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-02-28 19:45:50 +00:00
Weblate a55916998b Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/
2026-02-27 02:46:24 +00:00
Patch Notes GuyandWeblate cca2d54682 Translated using Weblate (Spanish (Ecuador))
Currently translated at 0.0% (0 of 0 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es_EC/
2026-02-27 02:44:12 +00:00
Weblate f5bd6a4054 Update translation files
Updated by "Remove blank strings" add-on in Weblate.

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/
2026-02-27 02:40:18 +00:00
Patch Notes GuyandWeblate 13466815e5 Added translation using Weblate (Spanish (Ecuador)) 2026-02-27 02:40:15 +00:00
Watuneni iteiteandWeblate e871047cf1 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-02-25 21:37:45 +00:00
Nico MerenqandWeblate a1de8c95f6 Translated using Weblate (Russian)
Currently translated at 79.5% (7206 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-02-24 17:47:11 +00:00
geumuandWeblate eb94bdb9c2 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-21 11:40:10 +00:00
arvid6andWeblate 509c2365bb Translated using Weblate (Spanish)
Currently translated at 94.0% (8522 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-20 23:12:50 +00:00
arvid6andWeblate 6271476ed3 Translated using Weblate (Spanish)
Currently translated at 93.2% (8445 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-20 10:57:20 +00:00
arvid6andWeblate f5cec10e3e Translated using Weblate (Spanish)
Currently translated at 93.2% (8445 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-20 10:13:25 +00:00
Watuneni iteiteandWeblate b6f20c1d75 Translated using Weblate (Japanese)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-02-19 06:59:35 +00:00
geumuandWeblate 750d583770 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-17 20:41:32 +00:00
arvid6andWeblate 05f0b439a8 Translated using Weblate (Spanish)
Currently translated at 91.9% (8332 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-17 20:41:32 +00:00
geumuandWeblate 1cb4ffd02f Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-17 17:11:34 +00:00
geumuandWeblate 8600d026c3 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-17 17:02:57 +00:00
Zack the GhastandWeblate 4e24c764de Translated using Weblate (Portuguese (Portugal))
Currently translated at 70.1% (6355 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-02-16 10:51:45 +00:00
arvid6andWeblate 192d40a475 Translated using Weblate (Spanish)
Currently translated at 91.9% (8328 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-13 15:54:44 +00:00
geumuandWeblate 8c86bb636d Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-13 12:12:26 +00:00
arvid6andWeblate 5dc43a8d58 Translated using Weblate (Spanish)
Currently translated at 91.8% (8318 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-13 12:12:25 +00:00
arvid6andWeblate 68a3d7e3b1 Translated using Weblate (Spanish)
Currently translated at 91.8% (8318 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-13 10:26:11 +00:00
Zack the GhastandWeblate 8f4b2c3c0a Translated using Weblate (Portuguese (Portugal))
Currently translated at 69.9% (6334 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-02-11 23:49:07 +00:00
arvid6andWeblate 6645cb68f7 Translated using Weblate (Spanish)
Currently translated at 91.7% (8316 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-11 23:49:06 +00:00
Zack the GhastandWeblate 4c5f330f2a Translated using Weblate (Portuguese (Portugal))
Currently translated at 69.4% (6289 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-02-11 02:22:03 +00:00
arvid6andWeblate 00d445b793 Translated using Weblate (Spanish)
Currently translated at 91.5% (8296 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-11 02:22:01 +00:00
geumuandWeblate cd236c2dea Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-10 10:12:12 +00:00
Iván Martín Quinteiro JourdanandWeblate aebf40f78c Translated using Weblate (Spanish)
Currently translated at 89.4% (8107 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-10 10:12:11 +00:00
arvid6andWeblate 52b28fb73f Translated using Weblate (Spanish)
Currently translated at 89.4% (8107 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-10 10:12:11 +00:00
Zack the GhastandWeblate 9b75f20fff Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.9% (6250 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-02-09 11:15:58 +00:00
Nico MerenqandWeblate 56b1fc1149 Translated using Weblate (Russian)
Currently translated at 78.9% (7154 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-02-08 16:22:17 +00:00
阿鱼andWeblate 662ca83a60 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-02-08 10:52:27 +00:00
Nico MerenqandWeblate d80ff055f5 Translated using Weblate (Russian)
Currently translated at 77.4% (7015 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-02-08 10:52:26 +00:00
arvid6andWeblate 898f48cf8f Translated using Weblate (Spanish)
Currently translated at 86.8% (7871 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/es/
2026-02-08 10:52:24 +00:00
geumuandWeblate 1688dd42b3 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-02-07 03:20:44 +00:00
geumuandWeblate c4e8fe9c74 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-01-31 15:46:02 +00:00
Zack the GhastandWeblate 3ecaf2dd13 Translated using Weblate (Portuguese (Portugal))
Currently translated at 68.7% (6227 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_PT/
2026-01-31 01:39:31 +00:00
geumuandWeblate 281afd74e6 Translated using Weblate (Korean)
Currently translated at 100.0% (9061 of 9061 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-01-31 01:39:30 +00:00
3174 changed files with 110272 additions and 20606 deletions
+2 -2
View File
@@ -71,8 +71,8 @@ setup_packsquash:
- echo "Installing wget & unzip"
- yum install -y wget unzip
- echo "Downloading PackSquash"
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.0/PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip
- unzip PackSquash.CLI.executable.x86_64-unknown-linux-musl.zip -d ./packsquash_bin
- wget https://github.com/ComunidadAylas/PackSquash/releases/download/v0.4.1/packsquash-x86_64-unknown-linux-musl.zip
- unzip packsquash-x86_64-unknown-linux-musl.zip -d ./packsquash_bin
- echo "Creating PackSquash options.toml"
- |
cat <<EOF > ./options.toml
+176 -25
View File
@@ -1,6 +1,4 @@
# Changelog
## [1.7.4 (MONTH Nth, 2026)](#1-7-4)
## [1.7.4 (MONTH Xth, 2026)](#1-7-4)
## [1.8.0 (MONTH Xth, 2026)](#1-8-0)
### Additions
@@ -54,40 +52,70 @@
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
- Added 7 new advancements
- Pressing the inventory key now closes the Pokémon interact wheel GUI
- Pokémon cries now play when clicking Pokémon in the summary screen.
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens
### New rideable Pokémon
- Drampa
- Archeops
- Skarmory
- Crobat
- Goodra
- Hisuian Goodra
- Milotic
### Pokémon Added
- Pancham (+ Cosmetic with Black Glasses)
- Pangoro (+ Cosmetic with Black Glasses)
- Skitty
- Delcatty
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Skitty
- Delcatty
- Seviper
- Zangoose
- Cherubi
- Cherrim
- Stunky
- Skuntank
- Audino
- Pawniard
- Bisharp
- Kingambit
- Pancham (+ Cosmetic with Black Glasses)
- Pangoro (+ Cosmetic with Black Glasses)
- Rockruff
- Lycanroc
- Rolycoly
- Carkol
- Coalossal
- Snom
- Frosmoth
- Duraludon
- Archaludon
- Applin
- Flapple
- Appletun
- Dipplin
- Hydrapple
- Nymble
- Lokix
#### Regional Forms
- Sandshrew (Alolan)
- Sandslash (Alolan)
- Seviper
- Zangoose
- Stunky
- Skuntank
- Pawniard
- Bisharp
- Kingambit
- Avalugg (Hisuian)
### Model updates for the following Pokémon
- Goomy
@@ -99,35 +127,77 @@
- Zubat
- Golbat
- Crobat
- Pidgey
- Pidgeotto
- Pidgeot
### Model updates for the following Pokémon
- Sandshrew
- Sandslash
- Nidoking
- Zubat
- Golbat
- Crobat
- Porygon
- Porygon2
- Porygon-Z
- Skarmory
- Jynx
- Popplio
- Brionne
- Primarina
- Fletchling
- Fletchinder
- Talonflame
- Sableye
- Mawile
- Goomy
- Sliggoo
- Goodra
- Nacli
- Naclestack
- Naclstack
- Garganacl
- Skarmory
#### Model changes to the regional bias resource pack:
- Added Mime Jr. (Galar Bias)
- Added Bergmite (Hisui Bias)
- Changed models for every existing regional bias
### Animation updates for the following Pokémon
- Drampa
- Archeops
- Skarmory
- Skitty
- Flygon
- Shellos
- Riolu
- Lucario
- Shellos
- Skitty
- Joltik
- Purrloin
- Liepard
- Flygon
- Joltik
- Galvantula
- Nidoking
- Fletchling
- Fletchinder
- Talonflame
- Archeops
- Drampa
- Sewaddle
- Swadloon
- Leavanny
- Goomy
- Sliggoo
- Goodra
- Shedinja
- Skwovet
- Greedent
#### Added sounds to the following moves
- Absorb
- Aurora Beam
- Double Team
- Draco Meteor
- Fire Punch
- Fire Spin
- Flame Charge
@@ -147,6 +217,7 @@
- Tail Whip
### Changes
- Stat increase and decrease particles have been changed, with the addition of alpha boost particles.
- Dragonite's mailbag cosmetic can now also be obtained with a chest in the cosmetic slot
### Fixes
@@ -154,6 +225,22 @@
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
- Fixed Litleo not blinking
- Fixed Garchomp T-posing when sleeping while out of battle.
- Sharp Beak can now be crafted
- Metal Alloy can now be crafted
- Ability Capsules can now be brewed
- Using an Ability Capsule will now return an Empty Bottle
- The following items can now be obtained by brushing the following Pokémon:
- Pink Wool - Wigglytuff
- White Wool - Swablu, Altaria
- Shed Shell - Ekans, Arbok, Dratini, Dragonair, Dunsparce, Treecko, Grovyle, Sceptile, Seviper, Scraggy, Scrafty, Helioptile, Heliolisk, Salandit, Salazzle, Sobble, Drizzile, Inteleon, Silicoblra, Sandaconda, Toxel, Toxtricity, Orthworm, Dudunsparce
- Phantom Membrane - Dreepy, Drakloak, Dragapult
- The following items can now be obtained by evolving the following Pokémon into any of their evolutions:
- Turtle Scute - Squirtle, Wartortle, Turtwig, Grotle, Tirtouga, Chewtle
- Shed Shell - Metapod, Kakuna, Ekans, Paras, Venonat, Krabby, Scyther, Kabuto, Dratini, Dragonair, Ledyba, Spinarak, Yanma, Pineco, Dunsparce, GLigar, Pupitar, Treecko, Grovyle, Silcoon, Cascoon, Surskit, Nincada, Trapinch, Vibrava, Corphish, Anorith, Bagon, Shelgon, Kricketot, Burmy, Combee, Skorupi, Swadloon, Whirlipede, Dwebble, Scraggy, Karrablast, Joltik, Larvesta, Spewpa, Clauncher, Helioptile, Charjabug, Crabrawler, Cutiefly, Dewpider, Salandit, Wimpod, Poipole, Sobble, Drizzile, Dottler, Silicobra, Toxel, Sizzlipede, Snom, Tarountula, Nymble, Rellor
- Saccharine Leaves now drop Tart, Sweet, or Syrupy Apples, depending on their honey coating stage, instead of vanilla Apples
- Fixed the height of the Tumblestone Bud items when held in hand, placed on head, viewed in a GUI, and placed in an Item Frame
- Refactored all advancements, removing several deemed unnecessary. Due to this refactor, all previously obtained advancements will be reset besides the root (for picking a starter Pokémon). We are sorry for the inconvience, but it makes the tab cleaner and simplifies future advancements.
- Restructured Botany Pots integration folder structure
- Decrease the default pastured mon wander radius to 32.
- Factor in the weight of Pokémon when calculating pushing forces between hitboxes.
@@ -166,14 +253,53 @@
- Cleaned up seat data. Removed all now unused fields from seats in all species files. Also made locator seat names explicit in the species files (however, this doesn't break the legacy format)
- Marks can now provide aspects.
- Added new shiny and Alpha chance configs
- Apricorns now make a new sound when harvested
- Lowered the volume of harvesting berries
- Added alternate versions of the basic PC Wallpapers based on the original design of Cobblemon's PC, before public release
- Improved compatibility with various mods
- Farmer's Delight's cutting board can now be used to strip Apricorn and Saccharine's logs, or salvage their planks from various blocks. It can also separate their boats from chests
- Farmer's Delight's cutting board can be used to get two white dye from Pep-Up Flowers and two magenta dye from Medicinal Leeks
- Farmer's Delight's cutting board can separate Tumblestone Blocks back into Tumblestones
- Farmer's Delight's cutting board can salvage Type Gems from their Gem Blocks
- Farmer's Delight's Onions, Tomatoes, and Cabbages can be used to cook Open-Faced Sandwiches. Nature's Spirit's Olives also can be used
- Farmer's Delight's cutting board can be used on Hearty Grains to obtain rice and straw, similarly to Farmer's Delight's Rice Panicles
- Farmer's Delight's Apple Pies and Apple Ciders can now be made using Applin Apples, alongside the Vanilla counterpart
- Amendments allows for cooking Leek and Potato Stew in a Cauldron
- Any mods that utilise Moonlight's Soft Fluid system can store Leek and Potato Stew, Medicinal Brew, Antidote, Awakening, Burn Heal, Ice Heal, Paralyze Heal, Full Heal, Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, Ability Capsules, Moomoo Milk, and Berry Juice in liquid form (such as Suplementaries' Jar or Goblet, or Cauldrons while using Amendments)
- Supplementaries' Hourglass can now use Bright Powder, Quick Powder, Metal Powder, Heal Powder, Silver Powder, and Remedy
- Berry Trees are now compatible with the Planter properly
- Saccharine Way Signs (added by Supplementaries automagically) now use a premade texture to prevent odd visual issues
- Supplementaries' Jar can now store various Cobblemon cookies, candies, and similar items
- Some of Supplementaries' items have been added to the `#cobblemon:held/container_held_items` item tag, including sacks, safes, and lunch baskets
- Experience Nuggets can be used in place of Sculk for making the EXP Candies if using Create
- Rage Candy Bar has a Create Mixing recipe
- All Cobblemon ores can now be crushed in Create's Crushing Wheels
- All Gem and Tumblestone Clusters can be crushed for automatic, low return, gems
- Hearty Grains can be milled like Farmer's Delight Rice Panicles if both Farmer's Delight and Create are installed
- Big Roots and Revival Herbs can be milled to their respective powders
- Pep-Up Flowers, Medicinal Leeks, and all 70 berries can be milled in Create's Millstone for additional dye drops
- Auspicious Armor and Upgrades can be Haunted using Create's crafting systems
- Applin Apples can be used to make Honeyed Apples in Create
- Golden Apples can now be crafted using the Applin Apples
- Prismarine Crystals and Amethyst Shards can now be used to craft Pokedexes
- Updated Pokedex Screens item tag to support tagged variants of Redstone and Glowstone Dust
- Sounds for interacting with various Cobblemon blocks now play from the center of the block, rather than the corner
- The `/pc` command no longer plays PC activation sounds
- Lowered the volume of Blitzle and Zebstrika's cry
- Fixed Raichu (male) and Alolan Raichu (both genders) not displaying the correct Partner Cap when Pewter Crunchies are in the cosmetic slot.
- Regional Foods will now prioritize being eaten instead of used for healing Pokémon, shift clicking will use it as healing
### Fixes
- Fixed Croagunk's walkspeed
- Fixed Blitzle's base scale being `1` rather than the intended `0.8`
- Fixed Magnezone's emissive layer for the shiny having the wrong name, thus displaying wrongly
- Fixed Litleo not blinking
- Fixed Pokédex layout for genderless variations
- Fixed odd stacking behaviour with the poke_bait item.
- Fixed an occasional crash on retrieving revived fossils
- Fixed a compatibility issue with World Game Rules which caused some log spam in specific cases.
- Fixed improper validation of Pokémon when performing a trade.
- Fixed an issue where particle like beam can disappear when you don't see the beginning of the beam, or you look away
- Fixed Bedrock particle materials (alpha/opaque/blend/add) sharing one render batch, which caused incorrect culling on mixed batches; each material now batches and draws with its own blend and cull state, and the alpha material renders as a true cutout
- Fixed an occasional crash on retrieving revived fossils
- Fixed brushing not working on Neoforge
- Fixed braking not working for submarines (whoops).
@@ -199,6 +325,16 @@
- Resolved the niche issue where Structures would spawn in an incorrect location, creating very funky world gen.
- Fixed Galarian Ponyta missing its cry.
- Fixed various log warnings caused by redundant files and sound events.
- Traded Pokémon will now retain the original friendship level when traded back to its OT
- Clusters now display correctly on maps
- Fixed removing mulch from berry trees with a shovel not working.
- Fixed collecting honey from Saccharine Leaf blocks deleting Honey Bottles when the inventory was full
- Fixed Stripped Saccharine Wood recipe being missing
- Fixed CancelableSoundInstance causing lag by attempting to create sounds every tick when out of range of hearing
- Fixed Stat Reducing Aprijuice not being possible to apply on a max out Pokémon, and the possibility of going into negatives
- Fixed Exploit where you could take out the bait or drop your Pokérod with bait to avoid consuming bait, now the bobber will just disappear if you do so
- Fixed Apricorn and Saccharine boats incorrectly using chest boat models
- Fixed wild and trainer battle AI refusing to select moves with the `adjacentAlly` target type, even when it's their only option
### Developer
- Added `PokeSnackSpawnerFactory` which allows influence registration on PokeSnack encounters
@@ -207,6 +343,8 @@
- Tweaked the spawning selector interface to take a bucket function rather than a bucket directly.
- Added a new `cobblemon:height_range` processor type to allow developers to restrict structure-spawning ranges.
- Separated all the functions of MoLangFunctions.kt into their own files.
- Spawn buckets and their base weights are now provided by the spawner, with different base weights for world, fishing, snack, and habitat spawn buckets.
- Removed "SpawnBucket" class in favour of just storing maps of bucket names to weights.
### Molang & Datapacks
- Added `moveset_builders` datapack folder for different styles of movesets.
@@ -226,6 +364,19 @@
- Added `q.world.spawn_loot_table_items(loot_table_id, x, y, z)` which spawns the items from the loot table at the position. (Use type: "minecraft:chest" for the loot table.)
- Added `q.pokemon.entity` to access the entity of Pokémon.
- Flamethrower and Seismic Toss had their action effect JSON cleaned up
- Ride sounds can now be set as exclusive to passengers
- Ride sounds no longer play when submerged unless specified with a new setting
- NPC Trainers can now call a `command` animation when issuing a command in battle.
- Added a q.failed function to move action effects.
- Herd spawned Pokémon have their levels controlled differently. The `levelRange` property now acts as a clamp instead of a filter, with a new `herdLevelRange` property to do the filter behaviour.
### Particles
### Localization
- Updated translations for:
- Japanese
- German
- Ecuadorian Spanish
- Simplified Chinese
- Traditional Chinese
- Korean
+4
View File
@@ -72,6 +72,10 @@ tasks.withType<Test> {
}
}
tasks.processResources {
exclude("data/**/README.md")
}
sourceSets {
main {
blossom {
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity;
import com.cobblemon.mod.common.block.entity.DiscShelfBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Holder;
import net.minecraft.sounds.SoundEvent;
@@ -29,7 +29,7 @@ public abstract class NoteBlockMixin {
private void cobblemon$triggerTMShelfSequencer(BlockState state, Level level, BlockPos pos, int type, int data, CallbackInfoReturnable<Boolean> cir) {
final BlockPos shelfPos = pos.below();
final BlockEntity blockEntity = level.getBlockEntity(shelfPos);
if (!(blockEntity instanceof TMShelfBlockEntity tmShelfBlockEntity)) {
if (!(blockEntity instanceof DiscShelfBlockEntity discShelfBlockEntity)) {
return;
}
@@ -43,7 +43,7 @@ public abstract class NoteBlockMixin {
final Holder<SoundEvent> holder = instrument.getSoundEvent();
soundEvent = holder.value();
}
tmShelfBlockEntity.onNoteBlockPulse(level, pos, soundEvent);
discShelfBlockEntity.onNoteBlockPulse(level, pos, soundEvent);
}
// Suppress vanilla note-block playback while it is on top of a TM Shelf.
@@ -139,23 +139,4 @@ public abstract class ServerEntityMixin {
CobblemonNetwork.INSTANCE.sendPacketToPlayer(player, packet);
}
}
@Inject(method = "sendPairingData", at = @At("TAIL"))
private void cobblemon$sendPairingSeatAssignments(
ServerPlayer player,
Consumer<Packet<ClientGamePacketListener>> consumer,
CallbackInfo ci
) {
if (!(entity instanceof PokemonEntity pokemon)) return;
if (pokemon.getOccupiedSeats().isEmpty()) return;
pokemon.getOccupiedSeats().forEach((seat, passenger) -> {
if (seat.getLocator() == null) return;
CobblemonNetwork.INSTANCE.sendPacketToPlayer(
player,
new ClientboundSeatAssignmentPacket(passenger.getId(), pokemon.getId(), seat.getLocator())
);
});
}
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.duck.ChannelDuck;
import com.mojang.blaze3d.audio.Channel;
@@ -33,7 +33,7 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val LECTERN: BlockEntityType<LecternBlockEntity> = this.create("lectern", BlockEntityType.Builder.of(::LecternBlockEntity, CobblemonBlocks.LECTERN).build(null))
@JvmField
val TM_SHELF: BlockEntityType<TMShelfBlockEntity> = this.create("tm_shelf", BlockEntityType.Builder.of(::TMShelfBlockEntity, CobblemonBlocks.TM_SHELF).build(null))
val DISC_SHELF: BlockEntityType<DiscShelfBlockEntity> = this.create("disc_shelf", BlockEntityType.Builder.of(::DiscShelfBlockEntity, CobblemonBlocks.DISC_SHELF).build(null))
@JvmField
val BERRY = this.create("berry", BlockEntityType.Builder.of(::BerryBlockEntity, *CobblemonBlocks.berries().values.toTypedArray()).build(null))
@@ -255,31 +255,31 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val POKE_SNACK = this.create("poke_snack", PokeSnackBlock(BlockBehaviour.Properties.of().forceSolidOn().strength(0.5F).sound(SoundType.WOOL).pushReaction(PushReaction.DESTROY).lightLevel { if (it.getValue(BlockStateProperties.LIT)) 3 else 0 }, true))
@JvmField
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
val TUMBLESTONE_CLUSTER = tumblestoneBlock("tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null, MapColor.TERRACOTTA_ORANGE)
@JvmField
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_TUMBLESTONE = tumblestoneBlock("large_budding_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, TUMBLESTONE_CLUSTER, MapColor.TERRACOTTA_ORANGE)
@JvmField
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE)
val MEDIUM_BUDDING_TUMBLESTONE = tumblestoneBlock("medium_budding_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_TUMBLESTONE, MapColor.TERRACOTTA_ORANGE)
@JvmField
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE)
val SMALL_BUDDING_TUMBLESTONE = tumblestoneBlock("small_budding_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_TUMBLESTONE, MapColor.TERRACOTTA_ORANGE)
@JvmField
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
val SKY_TUMBLESTONE_CLUSTER = tumblestoneBlock("sky_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null, MapColor.COLOR_LIGHT_BLUE)
@JvmField
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("large_budding_sky_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, SKY_TUMBLESTONE_CLUSTER, MapColor.COLOR_LIGHT_BLUE)
@JvmField
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE)
val MEDIUM_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("medium_budding_sky_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_SKY_TUMBLESTONE, MapColor.COLOR_LIGHT_BLUE)
@JvmField
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE)
val SMALL_BUDDING_SKY_TUMBLESTONE = tumblestoneBlock("small_budding_sky_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_SKY_TUMBLESTONE, MapColor.COLOR_LIGHT_BLUE)
@JvmField
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null)
val BLACK_TUMBLESTONE_CLUSTER = tumblestoneBlock("black_tumblestone_cluster", TumblestoneBlock.STAGE_3, 7, 3, null, MapColor.TERRACOTTA_BLACK)
@JvmField
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER)
val LARGE_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("large_budding_black_tumblestone", TumblestoneBlock.STAGE_2, 5, 3, BLACK_TUMBLESTONE_CLUSTER, MapColor.TERRACOTTA_BLACK)
@JvmField
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE)
val MEDIUM_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("medium_budding_black_tumblestone", TumblestoneBlock.STAGE_1, 4, 3, LARGE_BUDDING_BLACK_TUMBLESTONE, MapColor.TERRACOTTA_BLACK)
@JvmField
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE)
val SMALL_BUDDING_BLACK_TUMBLESTONE = tumblestoneBlock("small_budding_black_tumblestone", TumblestoneBlock.STAGE_0, 3, 4, MEDIUM_BUDDING_BLACK_TUMBLESTONE, MapColor.TERRACOTTA_BLACK)
@JvmField
val TUMBLESTONE_BLOCK = this.create("tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@@ -289,85 +289,85 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val BLACK_TUMBLESTONE_BLOCK = this.create("black_tumblestone_block", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_BLACK).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@JvmField
val TYPE_GEM_CORE = this.create("deepslate_crystal_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0F, 10.0F).requiresCorrectToolForDrops()))
val TYPE_GEM_CORE = this.create("deepslate_crystal_core", TypeGemCoreBlock(BlockBehaviour.Properties.of().strength(4.0F, 10.0F).requiresCorrectToolForDrops().mapColor(MapColor.DEEPSLATE)))
private val typeGemBlocks = mutableMapOf<ResourceLocation, Block>()
@JvmField
val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("normal_gem_block")
val TYPE_GEM_BLOCK_NORMAL = typeGemBlock("normal_gem_block", MapColor.QUARTZ)
@JvmField
val TYPE_GEM_BLOCK_FIRE = typeGemBlock("fire_gem_block")
val TYPE_GEM_BLOCK_FIRE = typeGemBlock("fire_gem_block", MapColor.FIRE)
@JvmField
val TYPE_GEM_BLOCK_WATER = typeGemBlock("water_gem_block")
val TYPE_GEM_BLOCK_WATER = typeGemBlock("water_gem_block", MapColor.WATER)
@JvmField
val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("electric_gem_block")
val TYPE_GEM_BLOCK_GRASS = typeGemBlock("grass_gem_block", MapColor.COLOR_LIGHT_GREEN)
@JvmField
val TYPE_GEM_BLOCK_GRASS = typeGemBlock("grass_gem_block")
val TYPE_GEM_BLOCK_ELECTRIC = typeGemBlock("electric_gem_block", MapColor.GOLD)
@JvmField
val TYPE_GEM_BLOCK_ICE = typeGemBlock("ice_gem_block")
val TYPE_GEM_BLOCK_ICE = typeGemBlock("ice_gem_block", MapColor.ICE)
@JvmField
val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("fighting_gem_block")
val TYPE_GEM_BLOCK_FIGHTING = typeGemBlock("fighting_gem_block", MapColor.COLOR_RED)
@JvmField
val TYPE_GEM_BLOCK_POISON = typeGemBlock("poison_gem_block")
val TYPE_GEM_BLOCK_POISON = typeGemBlock("poison_gem_block", MapColor.COLOR_PURPLE)
@JvmField
val TYPE_GEM_BLOCK_GROUND = typeGemBlock("ground_gem_block")
val TYPE_GEM_BLOCK_GROUND = typeGemBlock("ground_gem_block", MapColor.PODZOL)
@JvmField
val TYPE_GEM_BLOCK_FLYING = typeGemBlock("flying_gem_block")
val TYPE_GEM_BLOCK_FLYING = typeGemBlock("flying_gem_block", MapColor.COLOR_LIGHT_BLUE)
@JvmField
val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("psychic_gem_block")
val TYPE_GEM_BLOCK_PSYCHIC = typeGemBlock("psychic_gem_block", MapColor.COLOR_MAGENTA)
@JvmField
val TYPE_GEM_BLOCK_BUG = typeGemBlock("bug_gem_block")
val TYPE_GEM_BLOCK_BUG = typeGemBlock("bug_gem_block", MapColor.TERRACOTTA_LIGHT_GREEN)
@JvmField
val TYPE_GEM_BLOCK_ROCK = typeGemBlock("rock_gem_block")
val TYPE_GEM_BLOCK_ROCK = typeGemBlock("rock_gem_block", MapColor.TERRACOTTA_LIGHT_GRAY)
@JvmField
val TYPE_GEM_BLOCK_GHOST = typeGemBlock("ghost_gem_block")
val TYPE_GEM_BLOCK_GHOST = typeGemBlock("ghost_gem_block", MapColor.TERRACOTTA_MAGENTA)
@JvmField
val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("dragon_gem_block")
val TYPE_GEM_BLOCK_DRAGON = typeGemBlock("dragon_gem_block", MapColor.COLOR_BLUE)
@JvmField
val TYPE_GEM_BLOCK_DARK = typeGemBlock("dark_gem_block")
val TYPE_GEM_BLOCK_DARK = typeGemBlock("dark_gem_block", MapColor.TERRACOTTA_BLACK)
@JvmField
val TYPE_GEM_BLOCK_STEEL = typeGemBlock("steel_gem_block")
val TYPE_GEM_BLOCK_STEEL = typeGemBlock("steel_gem_block", MapColor.METAL)
@JvmField
val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("fairy_gem_block")
val TYPE_GEM_BLOCK_FAIRY = typeGemBlock("fairy_gem_block", MapColor.COLOR_PINK)
private val typeGemClusters = mutableMapOf<ResourceLocation, TypeGemClusterBlock>()
@JvmField
val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("normal_gem_cluster", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("normal_gem"))
val TYPE_GEM_CLUSTER_NORMAL = typeGemCluster("normal_gem_cluster", TYPE_GEM_BLOCK_NORMAL, cobblemonResource("normal_gem"), MapColor.QUARTZ)
@JvmField
val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("fire_gem_cluster", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"))
val TYPE_GEM_CLUSTER_FIRE = typeGemCluster("fire_gem_cluster", TYPE_GEM_BLOCK_FIRE, cobblemonResource("fire_gem"), MapColor.FIRE)
@JvmField
val TYPE_GEM_CLUSTER_WATER = typeGemCluster("water_gem_cluster", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"))
val TYPE_GEM_CLUSTER_WATER = typeGemCluster("water_gem_cluster", TYPE_GEM_BLOCK_WATER, cobblemonResource("water_gem"), MapColor.WATER)
@JvmField
val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("electric_gem_cluster", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"))
val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("grass_gem_cluster", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"), MapColor.COLOR_LIGHT_GREEN)
@JvmField
val TYPE_GEM_CLUSTER_GRASS = typeGemCluster("grass_gem_cluster", TYPE_GEM_BLOCK_GRASS, cobblemonResource("grass_gem"))
val TYPE_GEM_CLUSTER_ELECTRIC = typeGemCluster("electric_gem_cluster", TYPE_GEM_BLOCK_ELECTRIC, cobblemonResource("electric_gem"), MapColor.GOLD)
@JvmField
val TYPE_GEM_CLUSTER_ICE = typeGemCluster("ice_gem_cluster", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"))
val TYPE_GEM_CLUSTER_ICE = typeGemCluster("ice_gem_cluster", TYPE_GEM_BLOCK_ICE, cobblemonResource("ice_gem"), MapColor.ICE)
@JvmField
val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("fighting_gem_cluster", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"))
val TYPE_GEM_CLUSTER_FIGHTING = typeGemCluster("fighting_gem_cluster", TYPE_GEM_BLOCK_FIGHTING, cobblemonResource("fighting_gem"), MapColor.COLOR_RED)
@JvmField
val TYPE_GEM_CLUSTER_POISON = typeGemCluster("poison_gem_cluster", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"))
val TYPE_GEM_CLUSTER_POISON = typeGemCluster("poison_gem_cluster", TYPE_GEM_BLOCK_POISON, cobblemonResource("poison_gem"), MapColor.COLOR_PURPLE)
@JvmField
val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("ground_gem_cluster", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"))
val TYPE_GEM_CLUSTER_GROUND = typeGemCluster("ground_gem_cluster", TYPE_GEM_BLOCK_GROUND, cobblemonResource("ground_gem"), MapColor.PODZOL)
@JvmField
val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("flying_gem_cluster", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"))
val TYPE_GEM_CLUSTER_FLYING = typeGemCluster("flying_gem_cluster", TYPE_GEM_BLOCK_FLYING, cobblemonResource("flying_gem"), MapColor.COLOR_LIGHT_BLUE)
@JvmField
val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("psychic_gem_cluster", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"))
val TYPE_GEM_CLUSTER_PSYCHIC = typeGemCluster("psychic_gem_cluster", TYPE_GEM_BLOCK_PSYCHIC, cobblemonResource("psychic_gem"), MapColor.COLOR_MAGENTA)
@JvmField
val TYPE_GEM_CLUSTER_BUG = typeGemCluster("bug_gem_cluster", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"))
val TYPE_GEM_CLUSTER_BUG = typeGemCluster("bug_gem_cluster", TYPE_GEM_BLOCK_BUG, cobblemonResource("bug_gem"), MapColor.TERRACOTTA_LIGHT_GREEN)
@JvmField
val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("rock_gem_cluster", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"))
val TYPE_GEM_CLUSTER_ROCK = typeGemCluster("rock_gem_cluster", TYPE_GEM_BLOCK_ROCK, cobblemonResource("rock_gem"), MapColor.TERRACOTTA_LIGHT_GRAY)
@JvmField
val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("ghost_gem_cluster", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"))
val TYPE_GEM_CLUSTER_GHOST = typeGemCluster("ghost_gem_cluster", TYPE_GEM_BLOCK_GHOST, cobblemonResource("ghost_gem"), MapColor.TERRACOTTA_MAGENTA)
@JvmField
val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("dragon_gem_cluster", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"))
val TYPE_GEM_CLUSTER_DRAGON = typeGemCluster("dragon_gem_cluster", TYPE_GEM_BLOCK_DRAGON, cobblemonResource("dragon_gem"), MapColor.COLOR_BLUE)
@JvmField
val TYPE_GEM_CLUSTER_DARK = typeGemCluster("dark_gem_cluster", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"))
val TYPE_GEM_CLUSTER_DARK = typeGemCluster("dark_gem_cluster", TYPE_GEM_BLOCK_DARK, cobblemonResource("dark_gem"), MapColor.TERRACOTTA_BLACK)
@JvmField
val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("steel_gem_cluster", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"))
val TYPE_GEM_CLUSTER_STEEL = typeGemCluster("steel_gem_cluster", TYPE_GEM_BLOCK_STEEL, cobblemonResource("steel_gem"), MapColor.METAL)
@JvmField
val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("fairy_gem_cluster", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"))
val TYPE_GEM_CLUSTER_FAIRY = typeGemCluster("fairy_gem_cluster", TYPE_GEM_BLOCK_FAIRY, cobblemonResource("fairy_gem"), MapColor.COLOR_PINK)
@JvmField
val POLISHED_TUMBLESTONE = this.create("polished_tumblestone", Block(BlockBehaviour.Properties.of().mapColor(MapColor.TERRACOTTA_ORANGE).strength(1.0F).sound(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS).requiresCorrectToolForDrops().instrument(NoteBlockInstrument.BASEDRUM)))
@@ -558,7 +558,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val RELIC_COIN_POUCH = create("relic_coin_pouch", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_POUCH_SOUNDS).pushReaction(PushReaction.DESTROY).noOcclusion(), true))
@JvmField
val RELIC_COIN_SACK = create("relic_coin_sack", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_SACK_SOUNDS).pushReaction(PushReaction.DESTROY).strength(0.4F), false))
val RELIC_COIN_SACK = create("relic_coin_sack", CoinPouchBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.RELIC_COIN_SACK_SOUNDS).pushReaction(PushReaction.DESTROY).strength(0.4F).mapColor(MapColor.GOLD), false))
@JvmField
val GILDED_CHEST = create("gilded_chest", GildedChestBlock(BlockBehaviour.Properties.ofFullCopy(Blocks.CHEST).noOcclusion().sound(CobblemonSounds.GILDED_CHEST_SOUNDS), GildedChestBlock.Type.RED))
@@ -702,7 +702,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
val LECTERN = create("lectern", LecternBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.WOOD).strength(2.5F).ignitedByLava().lightLevel { if (it.getValue(LecternBlock.EMIT_LIGHT) as Boolean) 13 else 0 }))
@JvmField
val TM_SHELF = create("tm_shelf", TMShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
val DISC_SHELF = create("disc_shelf", DiscShelfBlock(BlockBehaviour.Properties.of().mapColor(MapColor.WOOD).instrument(NoteBlockInstrument.BASS).sound(SoundType.METAL).strength(2.5F)))
@JvmField
val DISPLAY_CASE = create("display_case", DisplayCaseBlock(BlockBehaviour.Properties.of().sound(CobblemonSounds.DISPLAY_CASE_SOUNDS).noOcclusion().pushReaction(PushReaction.BLOCK).mapColor(MapColor.STONE).strength(0.3F).requiresCorrectToolForDrops()))
@@ -777,8 +777,9 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(BlockBehaviour.Properties.of().mapColor(apricorn.mapColor()).randomTicks().strength(Blocks.OAK_LOG.defaultDestroyTime(), Blocks.OAK_LOG.explosionResistance).sound(SoundType.WOOD).noOcclusion(), apricorn))
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?) : Block {
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?, mapColor: MapColor) : Block {
return this.create(name, TumblestoneBlock(BlockBehaviour.Properties.of()
.mapColor(mapColor)
.pushReaction(PushReaction.DESTROY)
.noOcclusion()
.strength(1.5F)
@@ -1008,8 +1009,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
return this.create(name, block)
}
private fun typeGemBlock(name: String): Block {
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().strength(3.0F, 6.0F).sound(CobblemonSounds.TYPE_GEM_BLOCK_SOUNDS)))
private fun typeGemBlock(name: String, mapColor: MapColor): Block {
val typeGemBlock = this.create(name, Block(BlockBehaviour.Properties.of().mapColor(mapColor).strength(3.0F, 6.0F).sound(CobblemonSounds.TYPE_GEM_BLOCK_SOUNDS)))
val identifier = cobblemonResource(name)
typeGemBlocks[identifier] = typeGemBlock
@@ -1019,8 +1020,8 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
fun typeGemBlocks() = typeGemBlocks.toMap()
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation): Block {
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().strength(2.0F, 3.0F).sound(CobblemonSounds.TYPE_GEM_CLUSTER_SOUNDS).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
private fun typeGemCluster(name: String, gemBlock: Block, dropItemId: ResourceLocation, mapColor: MapColor): Block {
val gemClusterBlock = this.create(name, TypeGemClusterBlock(BlockBehaviour.Properties.of().mapColor(mapColor).strength(2.0F, 3.0F).sound(CobblemonSounds.TYPE_GEM_CLUSTER_SOUNDS).pushReaction(PushReaction.DESTROY), gemBlock, dropItemId))
TypeGemClusterBlock.gemToClusterMap[gemBlock] = gemClusterBlock
@@ -1031,4 +1032,4 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
}
fun typeGemClusters() = typeGemClusters.toMap()
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.mojang.brigadier.arguments.ArgumentType
import kotlin.reflect.KClass
import net.minecraft.commands.synchronization.ArgumentTypeInfo
import net.minecraft.core.HolderLookup
import net.minecraft.resources.ResourceKey
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.MinecraftServer
@@ -168,12 +169,16 @@ interface CobblemonImplementation {
* TODO
*
* @param identifier
* @param reloader
* @param reloaderFactory
* @param type
* @param dependencies
*/
fun registerResourceReloader(identifier: ResourceLocation, reloader: PreparableReloadListener, type: PackType, dependencies: Collection<ResourceLocation>)
fun registerResourceReloader(
identifier: ResourceLocation,
type: PackType,
dependencies: Collection<ResourceLocation>,
reloaderFactory: (HolderLookup.Provider) -> PreparableReloadListener
)
/**
* TODO
*
@@ -460,7 +460,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
@JvmField
val TM_SHELF = blockItem("tm_shelf", CobblemonBlocks.TM_SHELF)
val DISC_SHELF = blockItem("disc_shelf", CobblemonBlocks.DISC_SHELF)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
@@ -1524,41 +1524,41 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
// Type Gems
@JvmField
val NORMAL_GEM = this.create("normal_gem", itemNameBlockItem("normal_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL))
val NORMAL_GEM = this.create("normal_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL))
@JvmField
val FIRE_GEM = this.create("fire_gem", itemNameBlockItem("fire_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE))
val FIRE_GEM = this.create("fire_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE))
@JvmField
val WATER_GEM = this.create("water_gem", itemNameBlockItem("water_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_WATER))
val WATER_GEM = this.create("water_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_WATER))
@JvmField
val GRASS_GEM = this.create("grass_gem", itemNameBlockItem("grass_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS))
val GRASS_GEM = this.create("grass_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS))
@JvmField
val ELECTRIC_GEM = this.create("electric_gem", itemNameBlockItem("electric_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC))
val ELECTRIC_GEM = this.create("electric_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC))
@JvmField
val ICE_GEM = this.create("ice_gem", itemNameBlockItem("ice_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_ICE))
val ICE_GEM = this.create("ice_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ICE))
@JvmField
val FIGHTING_GEM = this.create("fighting_gem", itemNameBlockItem("fighting_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING))
val FIGHTING_GEM = this.create("fighting_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING))
@JvmField
val POISON_GEM = this.create("poison_gem", itemNameBlockItem("poison_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_POISON))
val POISON_GEM = this.create("poison_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_POISON))
@JvmField
val GROUND_GEM = this.create("ground_gem", itemNameBlockItem("ground_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND))
val GROUND_GEM = this.create("ground_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND))
@JvmField
val FLYING_GEM = this.create("flying_gem", itemNameBlockItem("flying_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING))
val FLYING_GEM = this.create("flying_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING))
@JvmField
val PSYCHIC_GEM = this.create("psychic_gem", itemNameBlockItem("psychic_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC))
val PSYCHIC_GEM = this.create("psychic_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC))
@JvmField
val BUG_GEM = this.create("bug_gem", itemNameBlockItem("bug_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_BUG))
val BUG_GEM = this.create("bug_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_BUG))
@JvmField
val ROCK_GEM = this.create("rock_gem", itemNameBlockItem("rock_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK))
val ROCK_GEM = this.create("rock_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK))
@JvmField
val GHOST_GEM = this.create("ghost_gem", itemNameBlockItem("ghost_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST))
val GHOST_GEM = this.create("ghost_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST))
@JvmField
val DRAGON_GEM = this.create("dragon_gem", itemNameBlockItem("dragon_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON))
val DRAGON_GEM = this.create("dragon_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON))
@JvmField
val DARK_GEM = this.create("dark_gem", itemNameBlockItem("dark_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_DARK))
val DARK_GEM = this.create("dark_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_DARK))
@JvmField
val STEEL_GEM = this.create("steel_gem", itemNameBlockItem("steel_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL))
val STEEL_GEM = this.create("steel_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL))
@JvmField
val FAIRY_GEM = this.create("fairy_gem", itemNameBlockItem("fairy_gem", CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY))
val FAIRY_GEM = this.create("fairy_gem", GemItem(Item.Properties(), CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY))
//@JvmField
//val BINDING_SOIL = blockItem("binding_soil", CobblemonBlocks.BINDING_SOIL)
@@ -108,6 +108,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val LEVELUP = this.create("gui.levelup")
@JvmField
val MOVE_LEARN = this.create("gui.move_learn")
@JvmField
val PVN_BATTLE = this.create("battle.pvn.default")
@JvmField
@@ -142,6 +145,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val MONITOR_INSERT = this.create("block.monitor.insert")
@JvmField
val MONITOR_LOADING = this.create("block.monitor.loading")
@JvmField
val MONITOR_GLITCHING = this.create("block.monitor.glitching")
@JvmField
val FOSSIL_MACHINE_ACTIVATE = this.create("block.fossil_machine.activate")
@@ -210,17 +215,23 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val TM_USE = this.create("item.tm.use")
@JvmField
val TM_MACHINE_BURN_LOOP = this.create("block.tm_machine.burn_loop")
@JvmField
val TM_MACHINE_CRAFT = this.create("block.tm_machine.craft")
@JvmField
val TM_MACHINE_OFF = this.create("block.tm_machine.off")
@JvmField
val TM_MACHINE_ON = this.create("block.tm_machine.on")
val TM_MACHINE_START = this.create("block.tm_machine.start")
@JvmField
val TM_MACHINE_OPEN = this.create("block.tm_machine.open")
@JvmField
val TM_MACHINE_CLOSE = this.create("block.tm_machine.close")
@JvmField
val TM_MACHINE_PLACE_DISC = this.create("block.tm_machine.place_disc")
val TM_MACHINE_TM_INSERT = this.create("block.tm_machine.tm_insert")
@JvmField
val TM_MACHINE_TM_RETRIEVE = this.create("block.tm_machine.tm_retrieve")
@JvmField
val TM_MACHINE_ITEM_INSERT = this.create("block.tm_machine.item_insert")
@JvmField
val TM_MACHINE_ITEM_RETRIEVE = this.create("block.tm_machine.item_retrieve")
@JvmField
@@ -261,6 +272,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val BERRY_EAT_FULL = this.create("item.berry.eat.full")
@JvmField
val APRICORN_HARVEST = this.create("block.apricorn.harvest")
@JvmField
val BIG_ROOT_BREAK = this.create("block.big_root.break")
@JvmField
@@ -408,9 +422,9 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
)
@JvmField
val TYPE_GEM_BLOCK_BREAK = this.create("block.type_gem_cluster.break")
val TYPE_GEM_BLOCK_BREAK = this.create("block.type_gem_block.break")
@JvmField
val TYPE_GEM_CLUSTER_BREAK = this.create("block.type_gem_block.break")
val TYPE_GEM_CLUSTER_BREAK = this.create("block.type_gem_cluster.break")
@JvmField
val TYPE_GEM_BLOCK_HIT = this.create("block.type_gem_block.hit")
@JvmField
@@ -419,7 +433,7 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
val TYPE_GEM_BLOCK_STEP = this.create("block.type_gem_block.step")
@JvmField
val TYPE_GEM_CLUSTER_SOUNDS = SoundType(1f, 1.2f,
val TYPE_GEM_CLUSTER_SOUNDS = SoundType(1f, 1.1f,
TYPE_GEM_CLUSTER_BREAK,
TYPE_GEM_BLOCK_STEP,
TYPE_GEM_BLOCK_PLACE,
@@ -81,6 +81,9 @@ object CobblemonCriteria : PlatformRegistry<Registry<CriterionTrigger<*>>, Resou
@JvmField
val PLANT_TUMBLESTONE = this.create("plant_tumblestone", SimpleCriterionTrigger(PlantTumblestoneCriterion.CODEC))
@JvmField
val PLANT_TYPE_GEM = this.create("plant_type_gem", SimpleCriterionTrigger(PlantTypeGemCriterion.CODEC))
val RIDING_STAT_BOOST = this.create("riding_stat_boost", SimpleCriterionTrigger(RidingStatBoostCriterion.CODEC))
@JvmField
@@ -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.advancement.criterion
import com.cobblemon.mod.common.block.TypeGemClusterBlock
import com.cobblemon.mod.common.block.TypeGemCoreBlock
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.advancements.critereon.ContextAwarePredicate
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerPlayer
import java.util.*
class PlantTypeGemContext(val pos: BlockPos, val block: TypeGemClusterBlock)
class PlantTypeGemCriterion(
playerCtx: Optional<ContextAwarePredicate>
): SimpleCriterionCondition<PlantTypeGemContext>(playerCtx) {
companion object {
val CODEC: Codec<PlantTypeGemCriterion> = RecordCodecBuilder.create { it.group(
ContextAwarePredicate.CODEC.optionalFieldOf("player").forGetter(PlantTypeGemCriterion::playerCtx)
).apply(it, ::PlantTypeGemCriterion) }
}
override fun matches(player: ServerPlayer, context: PlantTypeGemContext): Boolean {
return player.level().getBlockState(context.pos).block is TypeGemCoreBlock
}
}
@@ -48,7 +48,7 @@ class RidingStatBoostCriterion(
val max = context.pokemon.pokemon.getMaxRideBoost(rideStat).toDouble()
val min = context.pokemon.pokemon.getBaseRideStat(rideStat).toDouble()
val value = context.pokemon.getRideStat(rideStat, ridingStyle, min, max)
val isSufficient = (value >= this.statValue && this.statValue > 0.0) || !context.pokemon.pokemon.canAddRideBoost(rideStat)
val isSufficient = (value >= this.statValue && this.statValue > 0.0) || !context.pokemon.pokemon.canApplyRideBoost(rideStat)
if (isSufficient && this.rideStat == "any") {
return true
} else if (!isSufficient && this.rideStat == "all") {
@@ -152,6 +152,11 @@ abstract class BattleActor(
open fun sendMessage(component: Component) {
sendUpdate(BattleMessagePacket(component))
}
/**
* Called when this actor's Pokemon uses a move. Default implementation does nothing.
* Subclasses (NPC-backed actors, players, etc) may override to trigger animations or UI.
*/
open fun onUseMove(user: BattlePokemon, target: BattlePokemon?) {}
open fun awardExperience(battlePokemon: BattlePokemon, experience: Int) {}
open fun sendUpdate(packet: NetworkPacket<*>) {}
@@ -1,33 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.data
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
// TODO this is implemented in multiple places but unused. Can we scratch this?
/**
* Represents an object that will possibly be synchronized to the client during datapack synchronization.
*
* @param T The type of the object being synchronized.
*
* @author Licious
* @since October 14th, 2022
*/
interface ClientDataSynchronizer<T> : Decodable, Encodable {
/**
* Compares an object of type [T] with this instance to see if synchronization is necessary.
*
* @param other The other instance of type [T].
* @return If synchronization is necessary.
*/
fun shouldSynchronize(other: T): Boolean
}
@@ -0,0 +1,93 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.data
import com.cobblemon.mod.common.util.endsWith
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import net.minecraft.core.HolderLookup
import java.io.BufferedReader
import java.io.File
import java.util.concurrent.ExecutionException
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.packs.PackType
import net.minecraft.server.packs.resources.ResourceManager
/**
* A [DataRegistry] that consumes JSON files.
* Every deserialized instance is attached to an [Identifier].
* For example a file under data/mymod/[resourcePath]/entry.json would be backed by the identifier modid:entry.
*
* @param T The type of the data consumed by this registry.
*
* @author Licious
* @since August 5th, 2022
*/
interface ServerJsonDataRegistry<T> : DataRegistry {
/**
* The [TypeToken] of type [T].
*/
val typeToken: TypeToken<T>
/**
* The folder location for the data this registry will consume.
*/
val resourcePath: String
override val type: PackType
get() = PackType.SERVER_DATA
override fun reload(manager: ResourceManager) {
//NO-OP, handled by reload(manager, registry)
}
fun reload(manager: ResourceManager, registry: HolderLookup.Provider) {
val gson = setupParser(registry)
val data = hashMapOf<ResourceLocation, T>()
manager.listResources(resourcePath) { path -> path.endsWith(JSON_EXTENSION) }.forEach { (identifier, resource) ->
if (identifier.namespace == "pixelmon") {
return@forEach
}
resource.open().use { stream ->
stream.bufferedReader().use { reader ->
val resolvedIdentifier = ResourceLocation.fromNamespaceAndPath(identifier.namespace, File(identifier.path).nameWithoutExtension)
data[resolvedIdentifier] = parse(reader, resolvedIdentifier, gson)
}
}
}
reload(data, registry)
}
fun parse(reader: BufferedReader, identifier: ResourceLocation, gson: Gson): T {
return try {
gson.fromJson(reader, typeToken.type)
} catch (exception: Exception) {
throw ExecutionException("Error loading JSON for data: $identifier", exception)
}
}
/**
* Allows the creation of [Gson] instances which have adapters that are registry-aware, e.g. [com.cobblemon.mod.common.pokemon.evolution.adapters.LegacyItemConditionWrapperAdapter]
*/
fun setupParser(registry: HolderLookup.Provider): Gson
/**
* Reloads this registry from the deserialized data.
*
* @param data A map of the data associating an instance to the respective identifier from the [ResourceManager].
*/
fun reload(data: Map<ResourceLocation, T>, registry: HolderLookup.Provider)
companion object {
const val JSON_EXTENSION = ".json"
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.events.entity
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
@@ -21,7 +20,7 @@ class SpawnBucketChosenEvent(
/** The cause of the spawn. */
val spawnCause: SpawnCause,
/** The bucket that was chosen. You can alter this. */
var bucket: SpawnBucket,
var bucket: String,
/** The final weights that were used to make the choice. */
val bucketWeights: Map<SpawnBucket, Float>
val bucketWeights: Map<String, Float>
)
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.events.fishing
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.world.item.ItemStack
@@ -27,7 +26,7 @@ interface BaitSpawnPokemonEvent {
*/
data class Pre(
val bobber: PokeRodFishingBobberEntity, // todo replace this with the bait entity
val chosenBucket: SpawnBucket,
val chosenBucket: String,
val rod: ItemStack
) : Cancelable(), BobberSpawnPokemonEvent
@@ -38,7 +37,7 @@ interface BaitSpawnPokemonEvent {
* @param pokemon The Pokemon that is modified.
*/
data class Modify(
val chosenBucket: SpawnBucket,
val chosenBucket: String,
val rod: ItemStack,
val pokemon: PokemonEntity
) : BobberSpawnPokemonEvent
@@ -52,7 +51,7 @@ interface BaitSpawnPokemonEvent {
*/
data class Post(
val bobber: PokeRodFishingBobberEntity,
val chosenBucket: SpawnBucket,
val chosenBucket: String,
val bait: ItemStack,
val pokemon: PokemonEntity
) : BobberSpawnPokemonEvent
@@ -8,12 +8,10 @@
package com.cobblemon.mod.common.api.events.fishing
import com.cobblemon.mod.common.api.spawning.SpawnBucket
/**
* Event that is fired when a bucket is chosen by the bobber.
* @param chosenBucket The bucket that is chosen.
* @param buckets The list of buckets that the bobber can choose from.
* @param luckOfTheSeaLevel The level of the Luck of the Sea enchantment on the rod.
*/
class BobberBucketChosenEvent(var chosenBucket: SpawnBucket, val buckets: MutableList<SpawnBucket>, val luckOfTheSeaLevel: Int)
class BobberBucketChosenEvent(var chosenBucket: String, val buckets: MutableList<String>, val luckOfTheSeaLevel: Int)
@@ -12,10 +12,7 @@ import com.bedrockk.molang.runtime.value.MoValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.entity.fishing.PokeRodFishingBobberEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -73,8 +70,8 @@ interface BobberSpawnPokemonEvent {
val bait: ItemStack,
val pokemon: PokemonEntity
) : BobberSpawnPokemonEvent {
val context = mutableMapOf<String, MoValue>(
"chosen_bucket" to ObjectValue(spawnAction.bucket, { it.name }),
val context = mutableMapOf(
"chosen_bucket" to StringValue(spawnAction.bucket),
"bait" to pokemon.level().itemRegistry.wrapAsHolder(bait.item).asMoLangValue(Registries.ITEM),
"pokemon_entity" to pokemon.asMoLangValue()
)
@@ -75,6 +75,7 @@ class SpawnBait(
val POKEMON_CHANCE = cobblemonResource("pokemon_chance")
val FRIENDSHIP = cobblemonResource("friendship")
val RARITY_BUCKET = cobblemonResource("rarity_bucket")
val SIZE = cobblemonResource("size")
fun registerEffect(type: ResourceLocation, effect: (PokemonEntity, Effect) -> Unit) {
EFFECT_FUNCTIONS[type] = effect
@@ -95,6 +96,7 @@ class SpawnBait(
registerEffect(HIDDEN_ABILITY_CHANCE) { entity, _ -> FishingSpawnCause.alterHAAttempt(entity) }
registerEffect(ALPHA_CHANCE) { entity, effect -> FishingSpawnCause.alterAlphaAttempt(entity) }
registerEffect(FRIENDSHIP) { entity, effect -> FishingSpawnCause.alterFriendshipAttempt(entity, effect) }
registerEffect(SIZE) { entity, effect -> FishingSpawnCause.alterSize(entity, effect) }
CobblemonEvents.BAIT_EFFECT_REGISTRATION.post(BaitEffectFunctionRegistryEvent()) { event ->
event.functions.forEach { (type, function) -> registerEffect(type, function) }
}
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.habitats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* A [HabitatPool] of [ActivatedHabitatSpawn]s. Mainly just to help with deserialization and ensuring
* a single type for everything that's applied to a habitat block.
*
* @author Hiroku
* @since February 13th, 2026
*/
class ActivatedHabitatPool : HabitatPool<ActivatedHabitatSpawn>() {
override val type: ResourceLocation = ActivatedHabitatSpawn.TYPE
override var spawns: List<ActivatedHabitatSpawn> = listOf()
companion object {
fun default() = ActivatedHabitatPool().apply {
id = ResourceLocation("cobblemon", "custom__default_activated_pool")
name = "Custom"
}
}
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
super.decode(buffer, buckets)
spawns = buffer.readList { ActivatedHabitatSpawn().apply { decode(buffer, buckets) } }
}
}
@@ -1,23 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.habitats
import com.cobblemon.mod.common.util.cobblemonResource
/**
* A basic [HabitatSpawn] that will be triggered via activated spawning in a habitat. Nothing to it, really.
*
* @author Hiroku
* @since February 13th, 2026
*/
class ActivatedHabitatSpawn : HabitatSpawn() {
companion object {
val TYPE = cobblemonResource("activated")
}
}
@@ -8,39 +8,81 @@
package com.cobblemon.mod.common.api.habitats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* A pool of [HabitatSpawn]s. This is either filled with [NaturalHabitatSpawn]s or [ActivatedHabitatSpawn]s depending
* on whether the pool is a [NaturalHabitatPool] or an [ActivatedHabitatPool]. The pool is responsible for converting
* A pool of [HabitatSpawn]s. This is filled with [HabitatSpawn]s. The pool is responsible for converting
* its spawns into [PokemonSpawnDetail]s.
*
* @author Hiroku
* @since February 13th, 2026
*/
abstract class HabitatPool<T : HabitatSpawn> {
class HabitatPool {
companion object {
val types = mutableMapOf(
ActivatedHabitatSpawn.TYPE to ActivatedHabitatPool::class.java,
NaturalHabitatSpawn.TYPE to NaturalHabitatPool::class.java
)
fun default() = HabitatPool().apply {
id = ResourceLocation("cobblemon", "custom__default_pool")
name = "Custom"
}
fun writePoolToNBT(pool: HabitatPool, nbt: CompoundTag) {
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
if (pool.id.path.startsWith("custom__")) {
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
val spawnsListNBT = ListTag()
for (spawn in pool.spawns) {
val spawnTag = CompoundTag()
spawn.writeToNBT(spawnTag)
spawnsListNBT.add(spawnTag)
}
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
}
}
fun readPoolFromNBT(nbt: CompoundTag): HabitatPool {
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
// It's one that was stored on the block
val pool = HabitatPool()
pool.id = poolId
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
val spawns = mutableListOf<HabitatSpawn>()
spawnsListNBT.forEach { spawnTag ->
try {
val spawn = HabitatSpawn().apply { readFromNBT(spawnTag as CompoundTag) }
spawns.add(spawn)
} catch (_: Exception) {
// If there's an error reading a spawn, skip it and log the error.
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
}
}
pool.spawns = spawns
return pool
} else {
return HabitatPools.habitatPoolsById[poolId] ?: HabitatPool.default()
}
}
}
lateinit var id: ResourceLocation
var name: String = ""
abstract val type: ResourceLocation
abstract var spawns: List<T>
var spawns: List<HabitatSpawn> = listOf()
/** Whether a pool is editable is just down to whether it's not from a datapack. */
@Transient
var editable: Boolean = false
open fun validate() {
fun validate() {
for (spawn in spawns) {
if (SpawnablePosition.getByName(spawn.spawnablePositionType) == null) {
throw IllegalArgumentException("$id mentions non-existent spawnable position type ${spawn.spawnablePositionType} on spawn for ${spawn.species}")
@@ -58,8 +100,9 @@ abstract class HabitatPool<T : HabitatSpawn> {
buffer.writeCollection(spawns) { _, it -> it.encode(buffer) }
}
open fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
fun decode(buffer: RegistryFriendlyByteBuf) {
id = buffer.readResourceLocation()
name = buffer.readUtf()
spawns = buffer.readList { HabitatSpawn().apply { decode(buffer) } }
}
}
@@ -12,16 +12,13 @@ import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.IntRanges
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.adapters.HabitatPoolAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.cobblemon.mod.common.util.adapters.IntRangesAdapter
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
import com.cobblemon.mod.common.util.adapters.SpawnBucketAdapter
import com.cobblemon.mod.common.util.adapters.SpeciesAdapter
import com.cobblemon.mod.common.util.adapters.pokemonPropertiesShortAdapter
import com.cobblemon.mod.common.util.cobblemonResource
@@ -36,18 +33,16 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.world.level.storage.LevelResource
object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
object HabitatPools : JsonDataRegistry<HabitatPool> {
override val id = cobblemonResource("habitat_pools")
override val type = PackType.SERVER_DATA
override val observable = SimpleObservable<HabitatPools>()
override val typeToken: TypeToken<HabitatPool<*>> = TypeToken.get(HabitatPool::class.java)
override val typeToken: TypeToken<HabitatPool> = TypeToken.get(HabitatPool::class.java)
override val resourcePath = "habitat_pools"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(HabitatPool::class.java, HabitatPoolAdapter)
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
.registerTypeAdapter(Species::class.java, SpeciesAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(TimeRange::class.java, TimeRange.adapter)
@@ -56,10 +51,10 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
.setPrettyPrinting()
.create()
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool<*>>()
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool<*>>()
private val datapacked = mutableMapOf<ResourceLocation, HabitatPool>()
private val worldSaved = mutableMapOf<ResourceLocation, HabitatPool>()
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool<*>>()
val habitatPoolsById = mutableMapOf<ResourceLocation, HabitatPool>()
private lateinit var worldSavedPoolsFolder: File
@@ -73,7 +68,7 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
worldSavedPoolsFolder.listFiles { it.endsWith(".json") }.forEach {
val identifier = ResourceLocation.parse(it.nameWithoutExtension)
val habitat = gson.fromJson<HabitatPool<*>>(it.reader())
val habitat = gson.fromJson<HabitatPool>(it.reader())
habitat.id = identifier
habitat.validate()
habitat.editable = true
@@ -82,7 +77,7 @@ object HabitatPools : JsonDataRegistry<HabitatPool<*>> {
setupHabitatPoolsById()
}
override fun reload(data: Map<ResourceLocation, HabitatPool<*>>) {
override fun reload(data: Map<ResourceLocation, HabitatPool>) {
datapacked.clear()
datapacked.putAll(data)
data.entries.forEach { (identifier, habitat) ->
@@ -10,9 +10,7 @@ package com.cobblemon.mod.common.api.habitats
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.IntRanges
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.spawning.condition.AppendageCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
@@ -30,9 +28,7 @@ import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
/**
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances. This is a
* very shallow class tree, with [ActivatedHabitatSpawn] being a trivial subclass but the other subclass being
* [NaturalHabitatSpawn] which introduces the need to supply a spawn bucket.
* A kind of simplified spawn definition for the purposes of spawning from [HabitatBlockEntity] instances.
*
* These have only a small subset of the properties that a regular [PokemonSpawnDetail] would have to align with the
* fact that these are configured from a simple GUI. There are limits to how many properties and conditions could be
@@ -46,11 +42,13 @@ import net.minecraft.network.RegistryFriendlyByteBuf
* @author Hiroku
* @since February 13th, 2026
*/
sealed class HabitatSpawn {
class HabitatSpawn {
/** The species that will be spawned. */
lateinit var species: Species
/** The kind of position the Pokémon will spawn in. Things like "grounded", "seafloor", "fishing". */
lateinit var spawnablePositionType: String
/** The bucket to spawn from. */
var bucket: String = "common"
/** Selection weight. */
var weight: Float = 1F
@@ -77,6 +75,7 @@ sealed class HabitatSpawn {
timeRange?.let { nbt.putString(DataKeys.HABITAT_POOL_SPAWN_TIMES, TimeRange.adapter.serialize(it)) }
minLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT, it) }
maxLight?.let { nbt.putInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT, it) }
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket)
}
open fun readFromNBT(nbt: CompoundTag) {
@@ -89,6 +88,7 @@ sealed class HabitatSpawn {
timeRange = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_TIMES)) TimeRange.adapter.deserialize(nbt.getString(DataKeys.HABITAT_POOL_SPAWN_TIMES)) else null
minLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MIN_LIGHT) else null
maxLight = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT)) nbt.getInt(DataKeys.HABITAT_POOL_SPAWN_MAX_LIGHT) else null
bucket = if (nbt.contains(DataKeys.HABITAT_POOL_SPAWN_BUCKET)) nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) else "common"
}
open fun encode(buffer: RegistryFriendlyByteBuf) {
@@ -101,12 +101,10 @@ sealed class HabitatSpawn {
buffer.writeNullable(timeRange) { _, value -> buffer.writeUtf(TimeRange.adapter.serialize(value)) }
buffer.writeNullable(minLight) { _, value -> buffer.writeInt(value) }
buffer.writeNullable(maxLight) { _, value -> buffer.writeInt(value) }
buffer.writeUtf(bucket)
}
open fun decode(
buffer: RegistryFriendlyByteBuf,
buckets: List<SpawnBucket>
) {
open fun decode(buffer: RegistryFriendlyByteBuf) {
species = PokemonSpecies.getByIdentifier(buffer.readResourceLocation())!!
spawnablePositionType = buffer.readUtf()
weight = buffer.readFloat()
@@ -116,6 +114,7 @@ sealed class HabitatSpawn {
timeRange = buffer.readNullable { _ -> TimeRange.adapter.deserialize(buffer.readUtf()) }
minLight = buffer.readNullable { _ -> buffer.readInt() }
maxLight = buffer.readNullable { _ -> buffer.readInt() }
bucket = buffer.readUtf()
}
/** Creates the [PokemonSpawnDetail] that will be used in the spawner. This is done per habitat block. */
@@ -138,7 +137,7 @@ sealed class HabitatSpawn {
spawnDetail.apply {
this.id = "habitat_${species.resourceIdentifier}"
this.pokemon = properties
this.bucket = BestSpawner.config.buckets.first { it.weight > 0 }
this.bucket = this@HabitatSpawn.bucket
this.weight = this@HabitatSpawn.weight
this.levelRange = levelRange
this.conditions = mutableListOf(condition)
@@ -1,37 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.habitats
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* A [HabitatPool] of [NaturalHabitatSpawn]s. Mainly just to help with deserialization and ensuring
* a single type for everything that's applied to a habitat block.
*
* @author Hiroku
* @since February 13th, 2026
*/
class NaturalHabitatPool : HabitatPool<NaturalHabitatSpawn>() {
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
override var spawns: List<NaturalHabitatSpawn> = listOf()
companion object {
fun default() = NaturalHabitatPool().apply {
id = ResourceLocation("cobblemon", "custom__default_natural_pool")
name = "Custom"
}
}
override fun decode(buffer: RegistryFriendlyByteBuf, buckets: List<SpawnBucket>) {
super.decode(buffer, buckets)
spawns = buffer.readList { NaturalHabitatSpawn().apply { decode(buffer, buckets) } }
}
}
@@ -1,63 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.habitats
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.nbt.CompoundTag
import net.minecraft.network.RegistryFriendlyByteBuf
/**
* A type of [HabitatSpawn] that spawns Pokémon as part of natural world spawning, and therefore
* needs to indicate the [SpawnBucket] it will be part of.
*
* @author Hiroku
* @since February 13th, 2026
*/
class NaturalHabitatSpawn : HabitatSpawn() {
companion object {
val TYPE = cobblemonResource("natural")
}
lateinit var bucket: SpawnBucket
override fun createSpawnDetail(habitatBlockEntity: HabitatBlockEntity): PokemonSpawnDetail? {
val spawnDetail = super.createSpawnDetail(habitatBlockEntity)
spawnDetail?.bucket = bucket
return spawnDetail
}
override fun writeToNBT(nbt: CompoundTag) {
super.writeToNBT(nbt)
nbt.putString(DataKeys.HABITAT_POOL_SPAWN_BUCKET, bucket.name)
}
override fun readFromNBT(nbt: CompoundTag) {
super.readFromNBT(nbt)
bucket = Cobblemon.bestSpawner.config.buckets.first { it.name == nbt.getString(DataKeys.HABITAT_POOL_SPAWN_BUCKET) }
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
super.encode(buffer)
buffer.writeUtf(bucket.name)
}
override fun decode(
buffer: RegistryFriendlyByteBuf,
buckets: List<SpawnBucket>
) {
super.decode(buffer, buckets)
val bucketName = buffer.readUtf()
bucket = buckets.first { it.name == bucketName }
}
}
@@ -8,9 +8,7 @@
package com.cobblemon.mod.common.api.habitats.dto
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import net.minecraft.network.RegistryFriendlyByteBuf
class ActivatedHabitatSettingsDTO() {
@@ -20,7 +18,6 @@ class ActivatedHabitatSettingsDTO() {
var spawnRange = 16
var maxSpawns = -1
var maxSpawnsPerActivation = 1
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(ActivatedHabitatPool.default())
constructor(activatedHabitatSpawning: ActivatedHabitatSpawning): this() {
this.chance = activatedHabitatSpawning.chance
@@ -29,7 +26,6 @@ class ActivatedHabitatSettingsDTO() {
this.spawnRange = activatedHabitatSpawning.spawnRange
this.maxSpawns = activatedHabitatSpawning.maxSpawns
this.maxSpawnsPerActivation = activatedHabitatSpawning.maxSpawnsPerActivation
this.habitatPool = HabitatPoolSettingsDTO(activatedHabitatSpawning.pool)
}
fun encode(buffer: RegistryFriendlyByteBuf) {
@@ -39,20 +35,14 @@ class ActivatedHabitatSettingsDTO() {
buffer.writeInt(spawnRange)
buffer.writeInt(maxSpawns)
buffer.writeInt(maxSpawnsPerActivation)
habitatPool.encode(buffer)
}
fun decode(
buffer: RegistryFriendlyByteBuf,
buckets: List<SpawnBucket>
) {
fun decode(buffer: RegistryFriendlyByteBuf) {
this.chance = buffer.readFloat()
this.trigger = buffer.readEnum(ActivatedHabitatSpawning.Trigger::class.java)
this.cancelledNaturalSpawningRange = buffer.readInt()
this.spawnRange = buffer.readInt()
this.maxSpawns = buffer.readInt()
this.maxSpawnsPerActivation = buffer.readInt()
this.habitatPool = HabitatPoolSettingsDTO()
habitatPool.decode(buffer, buckets)
}
}
@@ -9,30 +9,23 @@
package com.cobblemon.mod.common.api.habitats.dto
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
import com.cobblemon.mod.common.api.habitats.HabitatPool
import com.cobblemon.mod.common.api.habitats.HabitatPools
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
class HabitatPoolSettingsDTO() {
lateinit var id: ResourceLocation
var isReference = false
var isActivated = false
// Below properties are only there when it's not a reference, otherwise the handler will have to look them up from the id
var name: String? = null
var spawns: MutableList<HabitatSpawn>? = null
constructor(habitatPool: HabitatPool<*>): this() {
constructor(habitatPool: HabitatPool): this() {
this.id = habitatPool.id
this.isReference = !id.path.startsWith("custom__")
this.isActivated = habitatPool is ActivatedHabitatPool
if (!this.isReference) {
this.name = habitatPool.name
this.spawns = habitatPool.spawns.toMutableList()
@@ -42,7 +35,6 @@ class HabitatPoolSettingsDTO() {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeResourceLocation(id)
buffer.writeBoolean(isReference)
buffer.writeBoolean(isActivated)
if (!isReference) {
buffer.writeUtf(name!!)
buffer.writeCollection(spawns!!) { _, spawn ->
@@ -52,50 +44,32 @@ class HabitatPoolSettingsDTO() {
}
fun decode(
buffer: RegistryFriendlyByteBuf,
buckets: List<SpawnBucket>
buffer: RegistryFriendlyByteBuf
) {
this.id = buffer.readResourceLocation()
this.isReference = buffer.readBoolean()
this.isActivated = buffer.readBoolean()
if (!isReference) {
this.name = buffer.readUtf()
this.spawns = buffer.readList {
val spawn = if (isActivated) {
ActivatedHabitatSpawn()
} else {
NaturalHabitatSpawn()
}
spawn.decode(buffer, buckets)
val spawn = HabitatSpawn()
spawn.decode(buffer)
spawn
}.toMutableList()
}
}
fun toHabitatPool(): HabitatPool<*>? {
fun toHabitatPool(): HabitatPool? {
if (isReference) {
val pool = HabitatPools.habitatPoolsById[id]
if (pool == null) {
Cobblemon.LOGGER.error("Could not find habitat pool with id $id")
} else if (pool is ActivatedHabitatPool && !isActivated) {
Cobblemon.LOGGER.error("Pool with id $id is an activated habitat pool but was deserialized as a natural pool")
} else if (pool !is ActivatedHabitatPool && isActivated) {
Cobblemon.LOGGER.error("Pool with id $id is a natural habitat pool but was deserialized as an activated pool")
}
return pool
}
return if (isActivated) {
val pool = ActivatedHabitatPool()
pool.id = id
pool.name = name!!
pool.spawns = spawns!!.map { it as ActivatedHabitatSpawn }
pool
} else {
val pool = NaturalHabitatPool()
pool.id = id
pool.name = name!!
pool.spawns = spawns!!.map { it as NaturalHabitatSpawn }
pool
}
val pool = HabitatPool()
pool.id = id
pool.name = name!!
pool.spawns = spawns!!
return pool
}
}
@@ -9,9 +9,9 @@
package com.cobblemon.mod.common.api.habitats.dto
import com.cobblemon.mod.common.api.habitats.HabitatPhaseOrder
import com.cobblemon.mod.common.api.habitats.HabitatPool
import com.cobblemon.mod.common.api.habitats.spawningstyle.ActivatedHabitatSpawning
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
@@ -23,6 +23,7 @@ class HabitatSettingsDTO() {
var modifiers: String = ""
lateinit var mimicId: ResourceLocation
var isActivatedSpawning = false
var pool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(HabitatPool.default())
var activatedSettings: ActivatedHabitatSettingsDTO? = null
var naturalSettings: NaturalHabitatSettingsDTO? = null
@@ -32,6 +33,7 @@ class HabitatSettingsDTO() {
this.levelRange = habitatBlockEntity.levelRange
this.modifiers = habitatBlockEntity.modifiers.asString()
this.mimicId = habitatBlockEntity.mimicId
this.pool = HabitatPoolSettingsDTO(habitatBlockEntity.pool)
val style = habitatBlockEntity.spawningStyle
if (style is ActivatedHabitatSpawning) {
this.isActivatedSpawning = true
@@ -48,6 +50,7 @@ class HabitatSettingsDTO() {
buffer.writeInt(levelRange.last)
buffer.writeUtf(modifiers)
buffer.writeResourceLocation(mimicId)
pool.encode(buffer)
buffer.writeBoolean(isActivatedSpawning)
if (isActivatedSpawning) {
activatedSettings?.encode(buffer)
@@ -56,10 +59,7 @@ class HabitatSettingsDTO() {
}
}
fun decode(
buffer: RegistryFriendlyByteBuf,
buckets: List<SpawnBucket>
) {
fun decode(buffer: RegistryFriendlyByteBuf) {
this.numberOfPhases = buffer.readInt()
this.phaseOrder = buffer.readEnum(HabitatPhaseOrder::class.java)
val levelRangeStart = buffer.readInt()
@@ -67,16 +67,18 @@ class HabitatSettingsDTO() {
this.levelRange = levelRangeStart..levelRangeEnd
this.modifiers = buffer.readUtf()
this.mimicId = buffer.readResourceLocation()
this.pool = HabitatPoolSettingsDTO()
this.pool.decode(buffer)
if (buffer.readBoolean()) {
this.isActivatedSpawning = true
this.naturalSettings = null
val activatedSettings = ActivatedHabitatSettingsDTO()
activatedSettings.decode(buffer, buckets)
activatedSettings.decode(buffer)
this.activatedSettings = activatedSettings
} else {
this.activatedSettings = null
val naturalSettings = NaturalHabitatSettingsDTO()
naturalSettings.decode(buffer, buckets)
naturalSettings.decode(buffer)
this.naturalSettings = naturalSettings
}
}
@@ -8,35 +8,25 @@
package com.cobblemon.mod.common.api.habitats.dto
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
import com.cobblemon.mod.common.api.habitats.spawningstyle.NaturalHabitatSpawning
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import net.minecraft.network.RegistryFriendlyByteBuf
class NaturalHabitatSettingsDTO() {
var replaceSpawns = false
var rangeOfInfluence = 16
var habitatPool: HabitatPoolSettingsDTO = HabitatPoolSettingsDTO(NaturalHabitatPool.default())
constructor(naturalHabitatSpawning: NaturalHabitatSpawning): this() {
this.replaceSpawns = naturalHabitatSpawning.replaceSpawns
this.rangeOfInfluence = naturalHabitatSpawning.rangeOfInfluence
this.habitatPool = HabitatPoolSettingsDTO(naturalHabitatSpawning.pool)
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeBoolean(replaceSpawns)
buffer.writeInt(rangeOfInfluence)
habitatPool.encode(buffer)
}
fun decode(
buffer: RegistryFriendlyByteBuf,
buckets: List<SpawnBucket>
) {
fun decode(buffer: RegistryFriendlyByteBuf, ) {
this.replaceSpawns = buffer.readBoolean()
this.rangeOfInfluence = buffer.readInt()
this.habitatPool = HabitatPoolSettingsDTO()
habitatPool.decode(buffer, buckets)
}
}
@@ -8,17 +8,16 @@
package com.cobblemon.mod.common.api.habitats.spawningstyle
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.habitats.HabitatSpawnActivatedEvent
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatPool
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawn
import com.cobblemon.mod.common.api.habitats.ActivatedHabitatSpawningInfluence
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.spawner.FixedAreaSpawner
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.BlockPos
import net.minecraft.nbt.CompoundTag
import net.minecraft.server.level.ServerLevel
@@ -27,7 +26,7 @@ import net.minecraft.server.level.ServerLevel
* A habitat spawning style defined by being triggered by something. The activation trigger, [trigger], is either
* a redstone pulse, every tick, or every random tick.
*
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
* The spawn details are generated from the pool every time there is some change to the habitat block's settings,
* then those spawn details are used for spawning Pokémon when the habitat block is triggered.
*
* The [chance] property is applied after the trigger logic so that not every activation necessarily results in a spawn
@@ -37,6 +36,10 @@ import net.minecraft.server.level.ServerLevel
* @since February 14th, 2026
*/
class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
companion object {
val TYPE = cobblemonResource("activated")
}
enum class Trigger {
/** Requires a redstone pulse. */
REDSTONE,
@@ -46,7 +49,8 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
RANDOM_TICK
}
override val type = ActivatedHabitatSpawn.TYPE
@Transient
override val type = TYPE
/** The chance that an activation by the [trigger] will result in a spawn attempt. The spawn attempt may still fail. */
var chance = 1F
@@ -60,11 +64,7 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
var maxSpawns = -1
/** The maximum number of Pokémon that can spawn in a single activation. This will still be capped by [maxSpawns]. */
var maxSpawnsPerActivation = 1
/** The pool of spawns to choose from. */
var pool: ActivatedHabitatPool = ActivatedHabitatPool.default()
// This is cached here so that it doesn't have to be regenerated every time the block is triggered, updated when settings change.
var spawnDetails: List<SpawnDetail> = listOf()
lateinit var spawner: FixedAreaSpawner
override fun writeToNBT(nbt: CompoundTag) {
@@ -75,10 +75,6 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
nbt.putInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE, spawnRange)
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS, maxSpawns)
nbt.putInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION, maxSpawnsPerActivation)
val poolNBT = CompoundTag()
writePoolToNBT(pool, poolNBT)
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
}
override fun readFromNBT(nbt: CompoundTag) {
@@ -88,25 +84,18 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
spawnRange = nbt.getInt(DataKeys.HABITAT_ACTIVATED_SPAWN_RANGE)
maxSpawns = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS)
maxSpawnsPerActivation = nbt.getInt(DataKeys.HABITAT_ACTIVATED_MAX_SPAWNS_PER_ACTIVATION)
pool = HabitatSpawningStyle.readPoolFromNBT(
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
poolInitializer = ::ActivatedHabitatPool,
spawnInitializer = ::ActivatedHabitatSpawn,
defaultPool = ActivatedHabitatPool::default
)
}
fun generateSpawnDetails(world: ServerLevel, pos: BlockPos) {
spawnDetails = pool.createSpawnDetails(habitatBlockEntity)
spawner = FixedAreaSpawner(
name = "habitat_spawner_$pos",
world = world,
position = pos,
spawnPool = SpawnPool("habitat_block").also {
it.details.addAll(spawnDetails)
it.details.addAll(habitatBlockEntity.spawnDetails)
it.precalculate()
},
buckets = Cobblemon.bestSpawner.config.activatedHabitatBuckets,
maxPokemonPerChunk = 16F,
verticalRadius = spawnRange,
horizontalRadius = spawnRange
@@ -118,7 +107,7 @@ class ActivatedHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Hab
// Check if the spawn attempt should even happen based on chance.
if (chance < 1F && world.random.nextFloat() >= chance) {
return
} else if (spawnDetails.isEmpty()) {
} else if (habitatBlockEntity.spawnDetails.isEmpty()) {
// If there are no spawn details, then there's no point in trying to spawn anything.
return
}
@@ -8,15 +8,7 @@
package com.cobblemon.mod.common.api.habitats.spawningstyle
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.habitats.HabitatPool
import com.cobblemon.mod.common.api.habitats.HabitatPools
import com.cobblemon.mod.common.api.habitats.HabitatSpawn
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.Tag
import net.minecraft.resources.ResourceLocation
/**
@@ -33,47 +25,4 @@ sealed interface HabitatSpawningStyle {
fun writeToNBT(nbt: CompoundTag)
fun readFromNBT(nbt: CompoundTag)
fun writePoolToNBT(pool: HabitatPool<*>, nbt: CompoundTag) {
nbt.putString(DataKeys.HABITAT_POOL_ID, pool.id.toString())
if (pool.id.path.startsWith("custom__")) {
nbt.putString(DataKeys.HABITAT_POOL_NAME, pool.name)
val spawnsListNBT = ListTag()
for (spawn in pool.spawns) {
val spawnTag = CompoundTag()
spawn.writeToNBT(spawnTag)
spawnsListNBT.add(spawnTag)
}
nbt.put(DataKeys.HABITAT_POOL_SPAWNS, spawnsListNBT)
}
}
companion object {
fun <T : HabitatSpawn, P : HabitatPool<T>> readPoolFromNBT(nbt: CompoundTag, poolInitializer: () -> P, spawnInitializer: () -> T, defaultPool: () -> P): P {
var pool: P
val poolId = nbt.getString(DataKeys.HABITAT_POOL_ID).asIdentifierDefaultingNamespace()
if (nbt.contains(DataKeys.HABITAT_POOL_NAME)) {
// It's one that was stored on the block
pool = poolInitializer()
pool.id = poolId
pool.name = nbt.getString(DataKeys.HABITAT_POOL_NAME)
val spawnsListNBT = nbt.getList(DataKeys.HABITAT_POOL_SPAWNS, Tag.TAG_COMPOUND.toInt())
val spawns = mutableListOf<T>()
spawnsListNBT.forEach { spawnTag ->
try {
val spawn = spawnInitializer().apply { readFromNBT(spawnTag as CompoundTag) }
spawns.add(spawn)
} catch (_: Exception) {
// If there's an error reading a spawn, skip it and log the error.
Cobblemon.LOGGER.error("Error reading activated habitat spawn from NBT for habitat block, skipping.")
}
}
pool.spawns = spawns
} else {
pool = HabitatPools.habitatPoolsById[poolId] as? P ?: defaultPool()
}
return pool
}
}
}
@@ -8,21 +8,18 @@
package com.cobblemon.mod.common.api.habitats.spawningstyle
import com.cobblemon.mod.common.api.habitats.NaturalHabitatPool
import com.cobblemon.mod.common.api.habitats.NaturalHabitatSpawn
import com.cobblemon.mod.common.api.habitats.spawningstyle.HabitatSpawningStyle.Companion.readPoolFromNBT
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.block.habitat.HabitatBlockEntity
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.nbt.CompoundTag
import net.minecraft.resources.ResourceLocation
/**
* A habitat spawning style defined by tapping into regular spawning methods. This works by adding to the spawn list
* for spawning that's occurring nearby. When [replaceSpawns] is set to true, the original spawns will be
* removed completely in favour of the spawns provided in this habitat's [pool].
* removed completely in favour of the spawns provided in this habitat's pool.
*
* The spawn details are generated from the [pool] every time there is some change to the habitat block's settings,
* The spawn details are generated from the pool every time there is some change to the habitat block's settings,
* then those spawn details are used for spawning Pokémon when world spawning is occurring nearby.
*
* Whether or not this interactions with overworld or fishing spawns is determined by whether the habitat pool has
@@ -32,10 +29,13 @@ import net.minecraft.resources.ResourceLocation
* @since February 22nd, 2026
*/
class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : HabitatSpawningStyle {
companion object {
val TYPE = cobblemonResource("natural")
}
@Transient
override val type: ResourceLocation = NaturalHabitatSpawn.TYPE
override val type: ResourceLocation = TYPE
var pool: NaturalHabitatPool = NaturalHabitatPool.default()
var replaceSpawns = false
var rangeOfInfluence = 16
@@ -43,33 +43,19 @@ class NaturalHabitatSpawning(val habitatBlockEntity: HabitatBlockEntity) : Habit
var affectsFishing = false
@Transient
var affectsOverworld = false
@Transient
var spawnDetails: List<SpawnDetail> = listOf()
fun generateSpawnDetails() {
spawnDetails = pool.createSpawnDetails(habitatBlockEntity = habitatBlockEntity)
affectsFishing = spawnDetails.any { it.spawnablePositionType.name == "fishing" }
affectsOverworld = spawnDetails.any { it.spawnablePositionType.name != "fishing" }
affectsFishing = habitatBlockEntity.spawnDetails.any { it.spawnablePositionType.name == "fishing" }
affectsOverworld = habitatBlockEntity.spawnDetails.any { it.spawnablePositionType.name != "fishing" }
}
override fun writeToNBT(nbt: CompoundTag) {
nbt.putBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS, replaceSpawns)
nbt.putInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE, rangeOfInfluence)
val poolNBT = CompoundTag()
writePoolToNBT(pool, poolNBT)
nbt.put(DataKeys.HABITAT_POOL, poolNBT)
}
override fun readFromNBT(nbt: CompoundTag) {
replaceSpawns = nbt.getBoolean(DataKeys.HABITAT_NATURAL_REPLACE_SPAWNS)
rangeOfInfluence = nbt.getInt(DataKeys.HABITAT_NATURAL_RANGE_OF_INFLUENCE)
pool = readPoolFromNBT(
nbt = nbt.getCompound(DataKeys.HABITAT_POOL),
poolInitializer = ::NaturalHabitatPool,
spawnInitializer = ::NaturalHabitatSpawn,
defaultPool = NaturalHabitatPool::default
)
}
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.api.interaction
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
@@ -26,6 +26,7 @@ import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.core.HolderLookup
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
@@ -36,28 +37,31 @@ import net.minecraft.world.level.block.Block
import net.minecraft.world.level.levelgen.structure.Structure
import net.minecraft.world.level.material.Fluid
object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
object PokemonInteractions : ServerJsonDataRegistry<PokemonInteractionSet> {
override val id = cobblemonResource("pokemon_interactions")
override val type = PackType.SERVER_DATA
override fun setupParser(registry: HolderLookup.Provider): Gson {
return GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter(registry))
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
}
override val observable = SimpleObservable<PokemonInteractions>()
override val typeToken: TypeToken<PokemonInteractionSet> = TypeToken.get(PokemonInteractionSet::class.java)
override val resourcePath = "pokemon_interactions"
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(ItemPredicate::class.java, LegacyItemConditionWrapperAdapter)
.registerTypeAdapter(Requirement::class.java, CobblemonRequirementAdapter)
.registerTypeAdapter(InteractionEffect::class.java, InteractionEffectAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Item::class.java).type, ItemLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Structure::class.java).type, StructureLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Fluid::class.java).type, FluidLikeConditionAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.setPrettyPrinting()
.create()
val interactions = mutableListOf<PokemonInteractionSet>()
@@ -65,7 +69,7 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
PokemonInteractionsSyncPacket(interactions).sendToPlayer(player)
}
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>) {
override fun reload(data: Map<ResourceLocation, PokemonInteractionSet>, registry: HolderLookup.Provider) {
interactions.clear()
val split = data.entries
.partition {
@@ -87,4 +91,4 @@ object PokemonInteractions : JsonDataRegistry<PokemonInteractionSet> {
.flatMap { it.interactions }
.firstOrNull(interactionCheck)
}
}
}
@@ -51,11 +51,15 @@ data class PokemonInteractionSet(
}
data class PokemonInteraction(
val grouping: ResourceLocation,
val requirements: List<Requirement> = listOf(),
val effects: List<InteractionEffect> = listOf(),
val cooldown: ExpressionLike = "0".asExpressionLike()
) {
// Not put as a necessary constructor parameter because then there isn't a default constructor
// and that means Gson just gives null for everything in the constructor and falls back to the JSON
// properties - completely ignoring the default values above.
lateinit var grouping: ResourceLocation
fun encode(buffer: FriendlyByteBuf) {
buffer.writeResourceLocation(grouping)
buffer.writeCollection(requirements) { it, requirement ->
@@ -76,11 +80,12 @@ data class PokemonInteraction(
val cooldown = buffer.readExpressionLike()
return PokemonInteraction(
grouping,
requirements,
effects,
cooldown,
)
).also {
it.grouping = grouping
}
}
}
}
@@ -38,7 +38,7 @@ import net.minecraft.world.phys.AABB
* @since July 29th, 2023
*/
interface PokemonSelectingItem {
fun use(player: ServerPlayer, stack: ItemStack): InteractionResultHolder<ItemStack> {
fun use(player: ServerPlayer, stack: ItemStack, ignoreShift: Boolean = false): InteractionResultHolder<ItemStack> {
val range = player.entityInteractionRange()
val entity = player.level()
.getEntities(player, AABB.ofSize(player.position(), range, range, range))
@@ -60,7 +60,7 @@ interface PokemonSelectingItem {
return interactWithSpecificBattle(player, stack, battlePokemon)
}
} ?: run {
if (!player.isShiftKeyDown) {
if (ignoreShift || !player.isShiftKeyDown) {
return if (entity != null) {
val pokemon = entity.pokemon
if (entity.ownerUUID == player.uuid) {
@@ -49,7 +49,12 @@ class FormDexRecord {
it.genders.addAll(genders.map(Gender::valueOf))
it.seenShinyStates.addAll(seenShinyStates)
it.highestLevel = highestLevel
it.knowledge = PokedexEntryProgress.valueOf(knowledge)
it.knowledge = when (knowledge) { // The string checks are due to legacy enum names which got changes in 1.8
"NONE" -> PokedexEntryProgress.UNREGISTERED
"ENCOUNTERED" -> PokedexEntryProgress.SEEN
"CAUGHT" -> PokedexEntryProgress.OWNED
else -> PokedexEntryProgress.valueOf(knowledge)
}
}
}
}
@@ -25,11 +25,4 @@ enum class PokedexEntryProgress : StringRepresentable, Comparable<PokedexEntryPr
OWNED;
override fun getSerializedName() = this.name
companion object {
val CODEC: Codec<PokedexEntryProgress> = RecordCodecBuilder.create { instance ->
instance.map ({
PokedexEntryProgress.valueOf(it)
}, PrimitiveCodec.STRING.fieldOf("name").forGetter { it.name })
}
}
}
@@ -56,10 +56,8 @@ class AggregatePokedexDef(
return speciesToEntry.values.toList()
}
}
override fun shouldSynchronize(other: PokedexDef) = true
override fun decode(buffer: RegistryFriendlyByteBuf) {
fun decode(buffer: RegistryFriendlyByteBuf) {
sortOrder = buffer.readSizedInt(IntSize.U_BYTE)
val size = buffer.readInt()
for (i in 0 until size) {
@@ -67,7 +65,7 @@ class AggregatePokedexDef(
}
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, sortOrder)
buffer.writeInt(subDexIds.size)
subDexIds.forEach {
@@ -8,8 +8,6 @@
package com.cobblemon.mod.common.api.pokedex.def
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.mojang.serialization.Codec
import com.mojang.serialization.MapCodec
@@ -23,7 +21,7 @@ import net.minecraft.resources.ResourceLocation
* @since August 24, 2024
* @author Apion
*/
abstract class PokedexDef: ClientDataSynchronizer<PokedexDef> {
abstract class PokedexDef {
//The ID used to find the codecs for this "type" of PokedexDef
abstract val typeId: ResourceLocation
//The ID of this dex in the Dexes registry
@@ -45,9 +45,7 @@ class SimplePokedexDef(
override fun getEntries() = entries.mapNotNull { DexEntries.entries[it] }
override fun shouldSynchronize(other: PokedexDef) = true
override fun decode(buffer: RegistryFriendlyByteBuf) {
fun decode(buffer: RegistryFriendlyByteBuf) {
sortOrder = buffer.readSizedInt(IntSize.U_BYTE)
val size = buffer.readInt()
for (i in 0 until size) {
@@ -55,7 +53,7 @@ class SimplePokedexDef(
}
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, sortOrder)
buffer.writeInt(entries.size)
entries.forEach {
@@ -41,16 +41,18 @@ class PokedexCategoryFilter(
PokedexCategoryFilterType.UNDISCOVERED_TM_MOVE -> hasUndiscoveredLevelUpTM(entry)
PokedexCategoryFilterType.RIDEABLE -> {
val species = PokemonSpecies.getByIdentifier(entry.speciesId)
species?.forms?.any { form -> !form.riding.behaviours.isNullOrEmpty() } == true
&& pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.OWNED
// we were silly as standard forms also can be rideable and we were ingoring those
(species?.forms.orEmpty() + listOfNotNull(species?.standardForm)).any { form -> !form.riding.behaviours.isNullOrEmpty() }
&& (pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.OWNED ||
pokedexManager.getHighestKnowledgeFor(entry) >= PokedexEntryProgress.SEEN)
}
}
}
private fun hasUndiscoveredLevelUpTM(entry: PokedexEntry): Boolean {
if (pokedexManager.getHighestKnowledgeFor(entry) != PokedexEntryProgress.OWNED) return false
if (pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.UNREGISTERED) return false
val species = PokemonSpecies.getByIdentifier(entry.speciesId) ?: return false
val forms = pokedexManager.getCaughtForms(entry)
val forms = pokedexManager.getEncounteredForms(entry)
val formData = if (forms.isEmpty()) {
listOf(species.standardForm)
} else {
@@ -72,7 +72,7 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
}
}
SearchByType.DROPS -> {
if (pokedexManager.getHighestKnowledgeFor(entry) !== PokedexEntryProgress.OWNED) return false
if (pokedexManager.getHighestKnowledgeFor(entry) == PokedexEntryProgress.UNREGISTERED) return false
val dropsList = mutableListOf<String>()
val formsList = if (species.forms.isEmpty()) mutableListOf(species.standardForm) else species.forms
formsList.forEach {
@@ -131,9 +131,7 @@ class SearchFilter(val pokedexManager: AbstractPokedexManager, val searchString:
form.moves.tutorMoves,
form.moves.eggMoves,
form.moves.evolutionMoves,
form.moves.formChangeMoves,
form.moves.specialMoves,
form.moves.legacyMoves
form.moves.formChangeMoves
)
for (moves in alwaysDiscovered) {
@@ -674,9 +674,6 @@ open class PokemonProperties {
@JvmOverloads
fun createEntity(world: Level, player: ServerPlayer? = null): PokemonEntity {
val pokemon = create(player)
if (pokemon.isAlpha) {
pokemon.initializeMovesetWithRandomTm()
}
return PokemonEntity(world, pokemon).also { applyCustomProperties(it) }
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.pokemon.moves
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.net.IntSize
@@ -18,7 +17,7 @@ import com.cobblemon.mod.common.util.writeSizedInt
import com.google.gson.JsonElement
import net.minecraft.network.RegistryFriendlyByteBuf
open class Learnset : ClientDataSynchronizer<Learnset> {
open class Learnset {
class Interpreter(val loadMove: (JsonElement, Learnset) -> Boolean) {
companion object {
fun parseFromPrefixIntoList(prefix: String, list: (Learnset) -> MutableList<MoveTemplate>): Interpreter {
@@ -111,9 +110,9 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
}
// We only sync level up moves atm
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
override fun decode(buffer: RegistryFriendlyByteBuf) {
fun decode(buffer: RegistryFriendlyByteBuf) {
this.levelUpMoves.clear()
repeat(times = buffer.readSizedInt(IntSize.U_BYTE)) {
val level = buffer.readSizedInt(IntSize.U_SHORT)
@@ -125,7 +124,7 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
}
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeSizedInt(IntSize.U_BYTE, levelUpMoves.size)
for ((level, moves) in levelUpMoves) {
buffer.writeSizedInt(IntSize.U_SHORT, level)
@@ -22,8 +22,8 @@ import net.minecraft.network.RegistryFriendlyByteBuf
*/
data class Seat(
val locator: String = "",
val condition: Expression?,
val poseAnimations: MutableList<SeatPoseAnimations>?,
val condition: Expression? = null,
val poseAnimations: MutableList<SeatPoseAnimations>? = null,
val handsBusy: Boolean = false,
) : Encodable {
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.snowstorm
import com.cobblemon.mod.common.client.render.shader.CobblemonShaders
import com.mojang.blaze3d.platform.GlStateManager
import com.mojang.blaze3d.systems.RenderSystem
import com.mojang.blaze3d.vertex.BufferBuilder
@@ -16,6 +15,7 @@ import com.mojang.blaze3d.vertex.DefaultVertexFormat
import com.mojang.blaze3d.vertex.Tesselator
import com.mojang.blaze3d.vertex.VertexFormat
import net.minecraft.client.particle.ParticleRenderType
import net.minecraft.client.renderer.GameRenderer
import net.minecraft.client.renderer.texture.TextureAtlas
import net.minecraft.client.renderer.texture.TextureManager
@@ -23,8 +23,9 @@ object ParticleMaterials {
val ALPHA = object : ParticleRenderType {
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
RenderSystem.enableBlend()
RenderSystem.enableCull()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_CUTOUT }
RenderSystem.setShader { GameRenderer.getParticleShader() }
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
@@ -36,10 +37,11 @@ object ParticleMaterials {
val ADD = object : ParticleRenderType {
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
RenderSystem.enableBlend()
RenderSystem.disableCull()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
RenderSystem.setShader { GameRenderer.getParticleShader() }
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE)
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
}
@@ -49,10 +51,11 @@ object ParticleMaterials {
val BLEND = object : ParticleRenderType {
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
RenderSystem.enableBlend()
RenderSystem.disableCull()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
RenderSystem.setShader { GameRenderer.getParticleShader() }
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
RenderSystem.blendFunc(GlStateManager.SourceFactor.DST_COLOR, GlStateManager.DestFactor.ZERO)
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA)
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
}
@@ -62,10 +65,11 @@ object ParticleMaterials {
val OPAQUE = object : ParticleRenderType {
override fun begin(tessellator: Tesselator, textureManager: TextureManager): BufferBuilder {
RenderSystem.enableBlend()
RenderSystem.enableCull()
RenderSystem.depthMask(true)
RenderSystem.setShader { CobblemonShaders.PARTICLE_BLEND }
RenderSystem.setShader { GameRenderer.getParticleShader() }
RenderSystem.setShaderTexture(0, TextureAtlas.LOCATION_PARTICLES)
RenderSystem.blendFunc(GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ONE)
RenderSystem.blendFunc(GlStateManager.SourceFactor.SRC_COLOR, GlStateManager.DestFactor.ZERO)
return tessellator.begin(VertexFormat.Mode.QUADS, DefaultVertexFormat.PARTICLE)
}
@@ -18,12 +18,12 @@ import com.cobblemon.mod.common.api.spawning.condition.SeafloorSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SubmergedSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SurfaceSpawningCondition
import com.cobblemon.mod.common.api.spawning.detail.EmptySpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.NPCSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonHerdSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.PokemonSpawnDetail
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.position.AreaSpawnablePositionResolver
import com.cobblemon.mod.common.api.spawning.position.FishingSpawnablePosition
@@ -121,6 +121,7 @@ object BestSpawner {
SpawnDetail.registerSpawnType(name = PokemonSpawnDetail.TYPE, PokemonSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = NPCSpawnDetail.TYPE, NPCSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = PokemonHerdSpawnDetail.TYPE, PokemonHerdSpawnDetail::class.java)
SpawnDetail.registerSpawnType(name = EmptySpawnDetail.TYPE, EmptySpawnDetail::class.java)
LOGGER.info("Loaded ${SpawnDetail.spawnDetailTypes.size} spawn detail types.")
loadConfig()
@@ -1,33 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning
/**
* A spawn bucket is a simple referenceable object by spawn files. The bucket is used at the beginning of
* the selection process to decide which set of spawns will be used for spawning on that attempt. Weights are used to
* make entire buckets more or less likely, and then weighted selections will occur within whichever bucket
* was chosen.
*
* Spawn buckets are configured in the main config.
*
* @author Hiroku
* @since June 20th, 2022
*/
class SpawnBucket() {
lateinit var name: String
var weight = 0F
constructor(name: String, weight: Float): this() {
this.name = name
this.weight = weight
}
override fun hashCode() = name.hashCode()
override fun equals(other: Any?) = other is SpawnBucket && other.name == name
}
@@ -49,7 +49,6 @@ object SpawnDetailPresets : JsonDataRegistry<SpawnDetailPreset> {
.setPrettyPrinting()
.setLenient()
.disableHtmlEscaping()
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
.registerTypeAdapter(SpawnablePositionType::class.java, RegisteredSpawnablePositionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Biome::class.java).type, BiomeLikeConditionAdapter)
.registerTypeAdapter(TypeToken.getParameterized(RegistryLikeCondition::class.java, Block::class.java).type, BlockLikeConditionAdapter)
@@ -67,7 +67,6 @@ object SpawnLoader {
.registerTypeAdapter(MoonPhaseRange::class.java, IntRangesAdapter(MoonPhaseRange.moonPhaseRanges) { MoonPhaseRange(*it) })
.registerTypeAdapter(ItemDropMethod::class.java, ItemDropMethod.adapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(SpawnBucket::class.java, SpawnBucketAdapter)
.registerTypeAdapter(CompoundTag::class.java, NbtCompoundAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(PossibleHeldItem::class.java, PossibleHeldItemAdapter)
@@ -8,10 +8,9 @@
package com.cobblemon.mod.common.api.spawning.condition
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.spawner.Spawner
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import net.minecraft.resources.ResourceLocation
/**
@@ -22,8 +21,8 @@ import net.minecraft.resources.ResourceLocation
* time as sharing that value. The requirement of implementations is only to get that
* key from both a [SpawnDetail] and a [SpawnablePosition].
*
* When a precalculation has been registered with a [Spawner] by adding to [Spawner.precalculators],
* the precalculation result will need to be updated by running [Spawner.precalculate]. That
* When a precalculation has been registered with a [SpawnPool] by adding to [SpawnPool.precalculators],
* the precalculation result will need to be updated by running [SpawnPool.precalculate]. That
* might take it a hot minute, but the result will be that there is an extra layer of
* shortcutting to the matching algorithm.
*
@@ -34,7 +33,7 @@ interface SpawningPrecalculation<T : Any> {
/** Retrieves the precalculation keys for the given spawn detail. */
fun select(detail: SpawnDetail): Collection<T>
/** Retrieves the precalculation key for the given spawnable position, if that key exists for this position. */
fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): T?
fun select(bucket: String, spawnablePosition: SpawnablePosition): T?
/**
* Generates a precalculation result for the list of [SpawnDetail]s and the subsequent precalculations.
@@ -74,7 +73,7 @@ interface SpawningPrecalculation<T : Any> {
object RootPrecalculation : SpawningPrecalculation<Any> {
val list = listOf(Unit)
override fun select(detail: SpawnDetail): List<Any> = list
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): Any = Unit
override fun select(bucket: String, spawnablePosition: SpawnablePosition): Any = Unit
}
/**
@@ -85,12 +84,12 @@ object RootPrecalculation : SpawningPrecalculation<Any> {
*/
object SpawnablePositionTypePrecalculation : SpawningPrecalculation<Class<out SpawnablePosition>> {
override fun select(detail: SpawnDetail) = listOf(detail.spawnablePositionType.clazz)
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = spawnablePosition::class.java
}
object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
object BucketPrecalculation : SpawningPrecalculation<String> {
override fun select(detail: SpawnDetail) = listOf(detail.bucket)
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = bucket
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = bucket
}
/**
@@ -99,7 +98,7 @@ object BucketPrecalculation : SpawningPrecalculation<SpawnBucket> {
*/
object BiomePrecalculation : SpawningPrecalculation<ResourceLocation> {
override fun select(detail: SpawnDetail) = detail.validBiomes
override fun select(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
override fun select(bucket: String, spawnablePosition: SpawnablePosition) = spawnablePosition.biomeHolder.unwrapKey().map { it.location() }.orElse(null)
}
/**
@@ -114,7 +113,7 @@ sealed class PrecalculationResult<T : Any>(
val calculation: SpawningPrecalculation<*>
) {
/** Gets the list of spawn details that fit this spawnable position. */
abstract fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>
abstract fun retrieve(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>
}
/**
@@ -127,7 +126,7 @@ class NestedPrecalculationResult<T : Any>(
calculation: SpawningPrecalculation<T>,
val mapping: Map<T, PrecalculationResult<*>> = mutableMapOf()
) : PrecalculationResult<T>(calculation) {
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
override fun retrieve(bucket: String, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)]?.retrieve(bucket, spawnablePosition) ?: emptyList()
}
/**
@@ -141,5 +140,5 @@ class FinalPrecalculationResult<T : Any>(
calculation: SpawningPrecalculation<*>,
val mapping: Map<T, List<SpawnDetail>>
) : PrecalculationResult<T>(calculation) {
override fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
override fun retrieve(bucket: String, spawnablePosition: SpawnablePosition) = mapping[calculation.select(bucket, spawnablePosition)] ?: emptyList()
}
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
/**
* Does a whole lot of nothing. Spawns nothing.
*
* @author Hiroku
* @since May 16th, 2026
*/
class EmptySpawnAction(
spawnablePosition: SpawnablePosition,
bucket: String,
detail: EmptySpawnDetail
) : SpawnAction<Unit>(spawnablePosition, bucket, detail) {
override fun run() {}
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
/**
* A spawn detail for nothing. It is used to take a slot, like to weigh down a spawn attempt,
* with something that ultimately doesn't spawn anything.
*
* The use case for this is very niche. With 1.8, the buckets that can be selected by a spawner
* during a spawn attempt will be narrowed down to the buckets that are used by the spawn
* pool. That means that if a bucket is never used by the pool, it will never be selected. That
* way, a bucket won't be selected when it never had any chance of being selected. This is what
* you would want in almost every case.
*
* However, in some cases you may want spawning to not spawn anything or you may want to preserve
* the relative rarity of the other buckets, and in that case you'd put something like this in a
* bucket so that it technically exists in the spawn pool without actually resulting in a spawned
* entity.
*
* @author Hiroku
* @since May 16th, 2026
*/
class EmptySpawnDetail : SpawnDetail() {
companion object {
val TYPE = "empty"
}
override val type: String = TYPE
override fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: String,
selectionData: SpawnSelectionData
) = EmptySpawnAction(
spawnablePosition = spawnablePosition,
bucket = bucket,
detail = this
)
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.resolveInt
@@ -20,7 +19,7 @@ import com.cobblemon.mod.common.util.resolveInt
* @author Hiroku
* @since October 8th, 2023
*/
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: SpawnBucket, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
class NPCSpawnAction(spawnablePosition: SpawnablePosition, bucket: String, override val detail: NPCSpawnDetail) : SingleEntitySpawnAction<NPCEntity>(spawnablePosition, bucket, detail) {
override fun createEntity(): NPCEntity {
val npc = NPCEntity(spawnablePosition.world)
npc.npc = detail.npcClass
@@ -11,13 +11,11 @@ package com.cobblemon.mod.common.api.spawning.detail
import com.bedrockk.molang.Expression
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.npc.NPCClass
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.util.asExpression
import com.google.gson.annotations.SerializedName
import kotlin.math.ceil
/**
* A [SpawnDetail] describing an [NPCEntity] spawn.
@@ -51,7 +49,7 @@ class NPCSpawnDetail : SpawnDetail() {
override fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
bucket: String,
selectionData: SpawnSelectionData
) = NPCSpawnAction(spawnablePosition, bucket, this)
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.util.weightedSelection
@@ -54,8 +53,10 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
var pokemon: PokemonProperties = PokemonProperties()
val levelRange: IntRange? = null
var levelRangeOffset: IntRange? = null
val herdLevelRange: IntRange? = null
var dropTable: DropTable? = null
var isLeader: Boolean? = null
var isLeader: Boolean = false
var isFollower: Boolean = true
var weight: Float = 1F
var maxTimes = 10
var heldItem: ResourceLocation? = null
@@ -83,7 +84,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
val herdable = getValidHerdMembers(selectionData)
val herdCounts = getHerdMemberCounts(selectionData)
val leaderIsPossible = herdable.any { it.levelRange?.contains(level) != false && it.isLeader == true }
val leaderIsPossible = herdable.any { it.herdLevelRange?.contains(level) != false && it.isLeader }
val leaderIsSelected = herdCounts.any { it.key?.isLeader == true && it.value > 0 }
return leaderIsPossible && !leaderIsSelected
}
@@ -98,14 +99,16 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
override fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
bucket: String,
selectionData: SpawnSelectionData
): PokemonSpawnAction {
val validHerdMembers = getValidHerdMembers(selectionData)
var searchedForLeader = false
val herdable = if (lacksPossibleLeader(selectionData)) {
validHerdMembers.filter { it.isLeader == true }
searchedForLeader = true
validHerdMembers.filter { it.isLeader }
} else {
validHerdMembers
validHerdMembers.filter { it.isFollower }
}.weightedSelection { it.weight } ?: herdablePokemon[0]
val level = selectionData.context[herdLevelKey] as? Int ?: 1
@@ -134,7 +137,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
heldItem = heldItem,
levelRange = levelRange
).also {
if (herdable.isLeader == true) {
if (herdable.isLeader && searchedForLeader) {
it.labels += HERD_LEADER
}
}
@@ -153,7 +156,7 @@ class PokemonHerdSpawnDetail : SpawnDetail() {
val count = counts[herdable] ?: 0
return@filter when {
count >= herdable.maxTimes -> false
herdable.levelRange != null && level !in herdable.levelRange -> false
herdable.herdLevelRange != null && level !in herdable.herdLevelRange -> false
else -> true
}
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.feature.SeasonFeatureHandler
@@ -26,7 +25,7 @@ import net.minecraft.world.item.ItemStack
*/
class PokemonSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
bucket: String,
override val detail: SpawnDetail,
/**
* The [PokemonProperties] that are about to be used. When using it, a copy will be taken.
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.api.drop.DropTable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.api.spawning.selection.SpawnSelectionData
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -104,7 +103,8 @@ class PokemonSpawnDetail : SpawnDetail() {
}
override fun createSpawnAction(
spawnablePosition: SpawnablePosition, bucket: SpawnBucket,
spawnablePosition: SpawnablePosition,
bucket: String,
selectionData: SpawnSelectionData
): SingleEntitySpawnAction<PokemonEntity> {
val levelRange = pokemon.deriveLevelRange(levelRange)
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.entity.SpawnEvent
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.world.entity.Entity
@@ -26,7 +25,7 @@ import net.minecraft.world.entity.MobSpawnType
*/
abstract class SingleEntitySpawnAction<T : Entity>(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
bucket: String,
detail: SpawnDetail
) : SpawnAction<EntitySpawnResult>(spawnablePosition, bucket, detail) {
abstract fun createEntity(): T?
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
import java.util.concurrent.CompletableFuture
@@ -21,7 +20,7 @@ import java.util.concurrent.CompletableFuture
*/
abstract class SpawnAction<R>(
val spawnablePosition: SpawnablePosition,
val bucket: SpawnBucket,
val bucket: String,
open val detail: SpawnDetail
) {
val labels = mutableListOf<String>()
@@ -15,7 +15,6 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.ModDependant
import com.cobblemon.mod.common.api.molang.MoLangFunctions.queryStructOf
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.condition.CompositeSpawningCondition
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
@@ -54,7 +53,7 @@ abstract class SpawnDetail : ModDependant {
var displayName: String? = null
@SerializedName("spawnablePositionType", alternate = ["context"])
lateinit var spawnablePositionType: SpawnablePositionType<*>
var bucket = SpawnBucket("", 0F)
var bucket = ""
var conditions = mutableListOf<SpawningCondition<*>>()
var anticonditions = mutableListOf<SpawningCondition<*>>()
var compositeCondition: CompositeSpawningCondition? = null
@@ -79,7 +78,7 @@ abstract class SpawnDetail : ModDependant {
"weight" to { DoubleValue(weight) },
"percentage" to { DoubleValue(percentage) },
"id" to { StringValue(id) },
"bucket" to { StringValue(bucket.name) },
"bucket" to { StringValue(bucket) },
"width" to { DoubleValue(width.toDouble()) },
"height" to { DoubleValue(height.toDouble()) },
"spawnable_position_type" to { StringValue(spawnablePositionType.name) },
@@ -136,6 +135,10 @@ abstract class SpawnDetail : ModDependant {
containsNullValues = true
LOGGER.error("Spawn Detail with id $id is invalid as it contains invalid values in its anticonditions (commonly caused by trailing comma in biomes or other arrays)")
}
if (bucket.isBlank()) {
containsNullValues = true
LOGGER.error("Spawn Detail with id $id is invalid as it does not have a bucket assigned")
}
return super.isModDependencySatisfied() && !containsNullValues
}
@@ -163,7 +166,7 @@ abstract class SpawnDetail : ModDependant {
*/
fun choose(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
bucket: String,
selectionData: SpawnSelectionData
): SpawnAction<*> {
val action = createSpawnAction(spawnablePosition, bucket, selectionData)
@@ -175,10 +178,10 @@ abstract class SpawnDetail : ModDependant {
return action
}
/** Instantiates an action at the given [SpawnablePosition] for the given [SpawnBucket]. */
/** Instantiates an action at the given [SpawnablePosition] for the given spawn bucket. */
protected abstract fun createSpawnAction(
spawnablePosition: SpawnablePosition,
bucket: SpawnBucket,
bucket: String,
selectionData: SpawnSelectionData
): SpawnAction<*>
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.detail
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnLoader
import com.cobblemon.mod.common.api.spawning.SpawnSet
import com.cobblemon.mod.common.api.spawning.condition.PrecalculationResult
@@ -61,12 +60,13 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
}
val details = mutableListOf<SpawnDetail>()
/** Tracks which buckets are in use by the pool, to prevent spawners for rolling buckets that don't exist. */
val buckets = mutableListOf<String>()
var precalculation: PrecalculationResult<*> = RootPrecalculation.generate(details, emptyList())
val precalculators = mutableListOf<SpawningPrecalculation<*>>()
override fun iterator() = details.iterator()
fun addPrecalculators(vararg precalculators: SpawningPrecalculation<*>): SpawnPool {
this.precalculators.addAll(precalculators)
precalculate()
@@ -88,6 +88,9 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
* be slow, especially if there are many precalculators and spawns.
*/
fun precalculate() {
buckets.clear()
buckets.addAll(details.asSequence().map(SpawnDetail::bucket).distinct())
if (precalculators.isEmpty()) {
precalculation = RootPrecalculation.generate(details, emptyList())
} else {
@@ -103,7 +106,7 @@ class SpawnPool(val name: String) : JsonDataRegistry<SpawnSet>, Iterable<SpawnDe
* to leverage the precalculation to get a smaller list of spawns as quickly
* as possible.
*/
fun retrieve(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
fun retrieve(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
return precalculation.retrieve(bucket, spawnablePosition)
}
@@ -70,6 +70,10 @@ class FishingSpawnCause(
pokemonEntity.pokemon.applyPotentialMarks(1.0 + (effect.value / 100))
}
fun alterSize(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
pokemonEntity.pokemon.scaleModifier *= effect.value.toFloat() / 1000.0f
}
fun saveDropsReroll(pokemonEntity: PokemonEntity, effect: SpawnBait.Effect) {
pokemonEntity.pokemon.forcedAspects += DROPS_REROLL_ASPECT
}
@@ -141,7 +145,7 @@ class FishingSpawnCause(
return
}
pokemonEntity.pokemon.isAlpha = true
pokemonEntity.pokemon.initializeMovesetWithRandomTm(2)
pokemonEntity.pokemon.initializeMovesetFromDefault()
if (!pokemonEntity.level().isClientSide) {
// Alpha status is applied after entity creation in bait flows, so rebuild the brain
// to ensure alpha-only sensors/memories are actually attached.
@@ -8,19 +8,19 @@
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.SpawnBucket
/**
* A [SpawningInfluence] that multiplies bucket weights by the provided values
* and then normalizes the resulting weights so that their total adds up to 100.
*
* No longer used by the mod, but kept for external suers.
*/
class BucketMultiplyingInfluence(
val multipliers: Map<String, Float>,
) : SpawningInfluence {
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
val updates = mutableMapOf<SpawnBucket, Float>()
override fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {
val updates = mutableMapOf<String, Float>()
for ((bucket, weight) in bucketWeights) {
val multiplier = multipliers[bucket.name]
val multiplier = multipliers[bucket]
if (multiplier != null) {
updates[bucket] = weight * multiplier
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import kotlin.math.pow
/**
@@ -33,7 +32,7 @@ open class BucketNormalizingInfluence(
val gradient: Float = 0.2F,
val firstTier: Float = 1.29F
) : SpawningInfluence {
override fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
override fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {
if (tier == 0) {
return
}
@@ -150,7 +150,7 @@ class SaccharineLogSlatheredInfluence(val pos: BlockPos? = null) : SpawningInflu
val chance = Cobblemon.config.honeySlatherAlphaChance
if (chance > 0 && Random.nextInt(chance) == 0) {
pokemon.isAlpha = true
pokemon.initializeMovesetWithRandomTm(2)
pokemon.initializeMovesetFromDefault()
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
@@ -39,9 +38,9 @@ interface SpawningInfluence {
/** Applies some influence over the entity that's been spawned. */
fun affectSpawn(action: SpawnAction<*>, entity: Entity) {}
/** Applies some influence over the weight of spawn buckets. */
fun affectBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {}
fun affectBucketWeights(bucketWeights: MutableMap<String, Float>) {}
/** Normalizes all bucket weights to add up to 100 */
fun normalizeBucketWeights(bucketWeights: MutableMap<SpawnBucket, Float>) {
fun normalizeBucketWeights(bucketWeights: MutableMap<String, Float>) {
val sum = bucketWeights.values.sum()
val to100 = 100F / sum
bucketWeights.keys.toList().forEach {
@@ -56,5 +55,5 @@ interface SpawningInfluence {
*
* You should make sure the spawn position satisfies any spawn details that you inject.
*/
fun injectSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail>? = null
fun injectSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail>? = null
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.spawning.preset
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
import com.cobblemon.mod.common.util.adapters.RegisteredSpawnablePositionAdapter
import com.google.gson.GsonBuilder
@@ -39,11 +38,34 @@ class BestSpawnerConfig {
"surface" to 0.01F,
"seafloor" to 1F
)
val buckets = mutableListOf(
SpawnBucket("common", 94.3F),
SpawnBucket("uncommon", 5F),
SpawnBucket("rare", 0.5F),
SpawnBucket("ultra-rare", 0.2F)
val worldBuckets = mutableMapOf(
"common" to 94F,
"uncommon" to 5F,
"rare" to 0.5F,
"ultra-rare" to 0.2F,
"boss" to 0.3F,
)
val fishingBuckets = mutableMapOf(
"common" to 83.25F,
"uncommon" to 11.25F,
"rare" to 4.125F,
"ultra-rare" to 1.375F
)
val activatedHabitatBuckets: MutableMap<String, Float> = mutableMapOf(
"common" to 83.25F,
"uncommon" to 11.25F,
"rare" to 4.125F,
"ultra-rare" to 1.375F
)
val pokeSnackBuckets = mutableMapOf(
"common" to 83.25F,
"uncommon" to 11.25F,
"rare" to 4.125F,
"ultra-rare" to 1.375F
)
companion object {
@@ -8,13 +8,12 @@
package com.cobblemon.mod.common.api.spawning.preset
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnDetailPresets
import com.cobblemon.mod.common.api.spawning.SpawnLoader
import com.cobblemon.mod.common.api.spawning.condition.SpawningCondition
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.multiplier.WeightMultiplier
import com.cobblemon.mod.common.api.spawning.position.SpawnablePositionType
import com.cobblemon.mod.common.util.MergeMode
import com.cobblemon.mod.common.util.adapters.SpawnDetailAdapter
import com.google.gson.JsonObject
@@ -41,7 +40,7 @@ import com.google.gson.annotations.SerializedName
* @since July 8th, 2022
*/
abstract class SpawnDetailPreset {
var bucket: SpawnBucket? = null
var bucket: String? = null
var spawnDetailType: String? = null
@SerializedName("spawnablePositionType", alternate = ["context"])
var spawnablePositionType: SpawnablePositionType<*>? = null
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.spawning.selection
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
@@ -88,12 +87,12 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
class SpawnablePositionSelectionData(
val spawner: Spawner,
val spawnablePositions: List<SpawnablePosition>,
val selectingSpawnInformation: MutableMap<SpawnBucket, SpawnablePositionBucketSelectionData>,
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData>,
): SpawnSelectionData {
override val spawnActions = mutableListOf<SpawnAction<*>>()
override val context = mutableMapOf<String, Any>()
fun getDataForBucket(bucket: SpawnBucket): SpawnablePositionBucketSelectionData {
fun getDataForBucket(bucket: String): SpawnablePositionBucketSelectionData {
val existing = selectingSpawnInformation[bucket]
if (existing != null) {
return existing
@@ -130,14 +129,14 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
spawner: Spawner,
spawnablePositions: List<SpawnablePosition>
): SpawnablePositionSelectionData {
val selectingSpawnInformation: MutableMap<SpawnBucket, SpawnablePositionBucketSelectionData> = mutableMapOf()
val selectingSpawnInformation: MutableMap<String, SpawnablePositionBucketSelectionData> = mutableMapOf()
return SpawnablePositionSelectionData(spawner, spawnablePositions, selectingSpawnInformation)
}
override fun selectSpawnAction(
spawner: Spawner,
bucket: SpawnBucket,
bucket: String,
selectionData: SpawnablePositionSelectionData,
): SpawnAction<*>? {
val selectingSpawnInformation = selectionData.getDataForBucket(bucket) ?: return null
@@ -149,7 +148,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
if (percentSum > 100) {
Cobblemon.LOGGER.warn(
"""
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket ${bucket.name}...
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket $bucket...
This means you don't understand how this option works.
""".trimIndent()
)
@@ -186,7 +185,7 @@ open class FlatSelector : SpawningSelector<FlatSelector.SpawnablePositionSelecti
override fun getTotalWeights(
spawner: Spawner,
bucket: SpawnBucket,
bucket: String,
spawnablePositions: List<SpawnablePosition>
): Map<SpawnDetail, Float> {
val selectionData = getSelectionData(spawner, spawnablePositions).getDataForBucket(bucket)
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.api.spawning.selection
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
@@ -105,7 +104,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
override val spawnActions = mutableListOf<SpawnAction<*>>()
override val context = mutableMapOf<String, Any>()
val bucketData = mutableMapOf<SpawnBucket, MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData>>()
val bucketData = mutableMapOf<String, MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData>>()
override fun removeSpawnDetails(shouldRemove: (SpawnDetail) -> Boolean) {
bucketData.values.forEach {
@@ -121,7 +120,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
}
}
fun getDataForBucket(bucket: SpawnBucket): MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData> {
fun getDataForBucket(bucket: String): MutableMap<SpawnablePositionType<*>, SpawnablePositionSelectionData> {
bucketData[bucket]?.let { return it }
val spawnablePositionTypeToSpawns = mutableMapOf<SpawnablePositionType<*>, SpawnablePositionSelectionData>()
@@ -149,7 +148,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
override fun selectSpawnAction(
spawner: Spawner,
bucket: SpawnBucket,
bucket: String,
selectionData: SeparatedSelectionData
): SpawnAction<*>? {
val bucketData = selectionData.getDataForBucket(bucket)
@@ -172,7 +171,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
if (percentSum > 100) {
LOGGER.warn(
"""
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket ${bucket.name}...
A spawn list for ${spawner.name} exceeded 100% on percentage sums in bucket $bucket...
This means you don't understand how this option works.
""".trimIndent()
)
@@ -244,7 +243,7 @@ open class FlatSpawnablePositionWeightedSelector : SpawningSelector<FlatSpawnabl
override fun getTotalWeights(
spawner: Spawner,
bucket: SpawnBucket,
bucket: String,
spawnablePositions: List<SpawnablePosition>
): Map<SpawnDetail, Float> {
val selectionData = getSelectionData(spawner, spawnablePositions).getDataForBucket(bucket)
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.api.spawning.selection
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.detail.SpawnAction
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
@@ -33,13 +32,13 @@ interface SpawningSelector<T : SpawnSelectionData> {
fun selectSpawnAction(
spawner: Spawner,
bucket: SpawnBucket,
bucket: String,
selectionData: T
): SpawnAction<*>?
fun select(
spawner: Spawner,
bucketSelector: () -> SpawnBucket,
bucketSelector: () -> String,
spawnablePositions: List<SpawnablePosition>,
maxSpawns: Int
): List<SpawnAction<*>> {
@@ -65,7 +64,7 @@ interface SpawningSelector<T : SpawnSelectionData> {
return spawnActions
}
fun getProbabilities(spawner: Spawner, bucket: SpawnBucket, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float> {
fun getProbabilities(spawner: Spawner, bucket: String, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float> {
val weights = getTotalWeights(spawner, bucket, spawnablePositions)
val totalWeight = weights.values.sum()
val percentages = mutableMapOf<SpawnDetail, Float>()
@@ -73,5 +72,5 @@ interface SpawningSelector<T : SpawnSelectionData> {
return percentages
}
fun getTotalWeights(spawner: Spawner, bucket: SpawnBucket, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float>
fun getTotalWeights(spawner: Spawner, bucket: String, spawnablePositions: List<SpawnablePosition>): Map<SpawnDetail, Float>
}
@@ -25,7 +25,8 @@ import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
open class BasicSpawner(
override val name: String,
override var spawnPool: SpawnPool,
override var maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk
override var maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk,
override val buckets: Map<String, Float>
) : Spawner {
override var selector: SpawningSelector<*> = SpawningSelector.DEFAULT
override var generator: SpawningZoneGenerator = Cobblemon.spawningZoneGenerator
@@ -8,11 +8,11 @@
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonPoiTypes
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence.Companion.TYPICAL_VARIATION
@@ -72,16 +72,9 @@ object FishingSpawnerFactory {
fun createSharedSpawner(): BasicSpawner {
return BasicSpawner(
name = "fishing",
spawnPool = spawnPool
).also { spawner ->
spawner.influences += BucketMultiplyingInfluence(
multipliers = mapOf(
"uncommon" to 2.25f,
"rare" to 5.5f,
"ultra-rare" to 5.5f,
)
)
}
spawnPool = spawnPool,
buckets = Cobblemon.bestSpawner.config.fishingBuckets,
)
}
fun findHabitatInfluences(world: ServerLevel, position: BlockPos, spawner: Spawner): Array<HabitatBlockEntity> {
@@ -11,8 +11,8 @@ package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import net.minecraft.server.level.ServerLevel
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
/**
* A spawner that works within a fixed area. Instances provide the center point and the radii and then the spawner
@@ -24,12 +24,13 @@ import net.minecraft.core.BlockPos
open class FixedAreaSpawner(
name: String,
spawnPool: SpawnPool,
buckets: Map<String, Float>,
val world: ServerLevel,
val position: BlockPos,
val horizontalRadius: Int,
val verticalRadius: Int,
maxPokemonPerChunk: Float = Cobblemon.config.pokemonPerChunk
) : BasicSpawner(name, spawnPool, maxPokemonPerChunk) {
) : BasicSpawner(name, spawnPool, maxPokemonPerChunk, buckets) {
fun run(cause: SpawnCause, maxSpawns: Int? = null): List<Any> {
return runForArea(
zoneInput = getZoneInput(cause),
@@ -8,20 +8,21 @@
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Cobblemon.config
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.util.getPlayer
import com.cobblemon.mod.common.util.nextBetween
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.Mth.PI
import net.minecraft.util.Mth.ceil
import java.util.*
import java.util.UUID
import kotlin.math.atan
import kotlin.math.cos
import kotlin.math.sin
import kotlin.random.Random
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.Mth.PI
import net.minecraft.util.Mth.ceil
/**
* A spawner that works around a single player. It will do basic tracking of a player's speed
@@ -35,7 +36,8 @@ open class PlayerSpawner(
spawnPool: SpawnPool
) : BasicSpawner(
name = player.name.string,
spawnPool = spawnPool
spawnPool = spawnPool,
buckets = Cobblemon.bestSpawner.config.worldBuckets
) {
val uuid: UUID = player.uuid
@@ -8,11 +8,11 @@
package com.cobblemon.mod.common.api.spawning.spawner
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.fishing.SpawnBait
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnPools
import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.BucketMultiplyingInfluence
import com.cobblemon.mod.common.api.spawning.influence.BucketNormalizingInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawnBaitInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
@@ -61,15 +61,6 @@ object PokeSnackSpawnerFactory {
firstTier = 1.2F
)
},
{ _ ->
BucketMultiplyingInfluence(
mapOf(
"uncommon" to 2.25f,
"rare" to 5.5f,
"ultra-rare" to 5.5f,
)
)
},
{ ctx ->
SpawnBaitInfluence(effects = ctx.baitEffects)
},
@@ -81,6 +72,7 @@ object PokeSnackSpawnerFactory {
return FixedAreaSpawner(
name = ctx.name,
spawnPool = spawnPool,
buckets = Cobblemon.bestSpawner.config.pokeSnackBuckets,
world = ctx.world,
position = ctx.pos,
horizontalRadius = ctx.horizontalRadius,
@@ -12,7 +12,6 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.entity.SpawnBucketChosenEvent
import com.cobblemon.mod.common.api.spawning.BestSpawner
import com.cobblemon.mod.common.api.spawning.SpawnBucket
import com.cobblemon.mod.common.api.spawning.SpawnCause
import com.cobblemon.mod.common.api.spawning.SpawningZoneGenerator
import com.cobblemon.mod.common.api.spawning.detail.EntitySpawnResult
@@ -26,6 +25,7 @@ import com.cobblemon.mod.common.api.spawning.position.calculators.SpawnablePosit
import com.cobblemon.mod.common.api.spawning.selection.SpawningSelector
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.isBoxLoaded
import com.cobblemon.mod.common.util.removeIf
import com.cobblemon.mod.common.util.squeezeWithinBounds
import com.cobblemon.mod.common.util.toVec3f
import com.cobblemon.mod.common.util.weightedSelection
@@ -52,6 +52,7 @@ import net.minecraft.world.phys.Vec3
interface Spawner {
val name: String
val influences: MutableList<SpawningInfluence>
val buckets: Map<String, Float>
var spawnPool: SpawnPool
var selector: SpawningSelector<*>
var generator: SpawningZoneGenerator
@@ -65,7 +66,7 @@ interface Spawner {
const val ENTITY_LIMIT_CHUNK_RANGE = 3
}
fun getMatchingSpawns(bucket: SpawnBucket, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
fun getMatchingSpawns(bucket: String, spawnablePosition: SpawnablePosition): List<SpawnDetail> {
val spawns = mutableListOf<SpawnDetail>()
spawns.addAll(spawnPool.retrieve(bucket, spawnablePosition).filter { it.isSatisfiedBy(spawnablePosition) })
spawnablePosition.influences.forEach { influence ->
@@ -172,11 +173,12 @@ interface Spawner {
fun copyInfluences() = influences.filter { !it.isExpired() }.toMutableList()
fun chooseBucket(cause: SpawnCause, influences: List<SpawningInfluence>): SpawnBucket {
val buckets = Cobblemon.bestSpawner.config.buckets
val bucketWeights = buckets.associateWith { it.weight }.toMutableMap()
fun chooseBucket(cause: SpawnCause, influences: List<SpawningInfluence>): String {
val bucketWeights = buckets.toMutableMap()
influences.forEach { it.affectBucketWeights(bucketWeights) }
val bucket = bucketWeights.entries.weightedSelection { it.value }?.key ?: buckets.first()
val usedBuckets = spawnPool.buckets.intersect(bucketWeights.keys)
bucketWeights.removeIf { it.key !in usedBuckets }
val bucket = bucketWeights.entries.weightedSelection { it.value }?.key ?: buckets.keys.first()
val event = SpawnBucketChosenEvent(
spawner = this,
spawnCause = cause,
@@ -45,11 +45,13 @@ object CobblemonBiomeTags {
@JvmField
val IS_END = create("is_end")
@JvmField
val IS_STONY_SHORE = create("is_stony_shore")
val IS_STONY_BEACH = create("is_stony_beach")
@JvmField
val IS_SOUL_FIRE = create("is_soul_fire")
val IS_SOUL_SAND = create("nether/is_soul_sand")
@JvmField
val IS_FUNGUS = create("is_fungus")
val IS_FUNGUS = create("nether/is_fungus")
@JvmField
val IS_WASTELAND = create("nether/is_wasteland")
@JvmField
val IS_GLACIAL = create("is_glacial")
@JvmField
@@ -68,7 +68,7 @@ object ShowdownInterpreter {
updateInstructionParser["-endability"] = { _, _, message, _ -> EndAbilityInstruction(message) }
updateInstructionParser["-end"] = { _, _, message, _ -> EndInstruction(message) }
updateInstructionParser["-enditem"] = { _, _, message, _ -> EndItemInstruction(message) }
updateInstructionParser["-fail"] = { _, _, message, _ -> FailInstruction(message) }
updateInstructionParser["-fail"] = { battle, _, message, _ -> FailInstruction(battle, message) }
updateInstructionParser["faint"] = { battle, _, message, _ -> FaintInstruction(battle, message) }
updateInstructionParser["-fieldactivate"] = { _, _, message, _ -> FieldActivateInstruction(message) }
updateInstructionParser["-fieldend"] = { _, _, message, _ -> FieldEndInstruction(message) }
@@ -38,15 +38,8 @@ class ShowdownThread : Thread("Cobblemon Showdown") {
override fun run() {
LOGGER.info("Starting showdown service...")
try {
ShowdownService.service.openConnection()
LOGGER.info("Showdown has been started!")
} catch (e: Exception) {
LOGGER.error("Failed to start showdown service!", e)
} finally {
this.latch.countDown()
throw RuntimeException("Showdown service failed to initialize...")
}
ShowdownService.service.openConnection()
LOGGER.info("Showdown has been started!")
this.latch.countDown()
}
}
@@ -18,6 +18,7 @@ import com.cobblemon.mod.common.battles.PassActionResponse
import com.cobblemon.mod.common.battles.ShowdownActionResponse
import com.cobblemon.mod.common.battles.ShowdownMoveset
import com.cobblemon.mod.common.battles.SwitchActionResponse
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
/**
* AI that randomly chooses a move from its moveset at a random target.
@@ -40,17 +41,19 @@ class RandomBattleAI : BattleAI {
return SwitchActionResponse(switchTo.uuid)
}
if (moveset == null) {
if (moveset == null)
return PassActionResponse
val move = moveset.moves.filter { it.canBeUsed() }.randomOrNull()
if (move == null) {
if (moveset.moves.any { it.id == "struggle" })
return MoveActionResponse("struggle")
else throw IllegalActionChoiceException(activeBattlePokemon.actor,
"No valid AI move choices, but Showdown never injected Struggle!")
}
val move = moveset.moves
.filter { it.canBeUsed() }
.filter { it.mustBeUsed() || it.target.targetList(activeBattlePokemon)?.isEmpty() != true }
.randomOrNull()
?: return MoveActionResponse("struggle")
val target = if (move.mustBeUsed()) null else move.target.targetList(activeBattlePokemon)
return if (target == null) {
return if (target.isNullOrEmpty()) {
MoveActionResponse(move.id)
} else {
// prioritize opponents rather than allies
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.battles.ai.strongBattleAI.AIUtility
import com.cobblemon.mod.common.battles.ai.strongBattleAI.ActiveTracker
import com.cobblemon.mod.common.battles.ai.strongBattleAI.TrackerPokemon
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import kotlin.random.Random
import kotlin.collections.associateWith
@@ -94,14 +95,14 @@ class StrongBattleAI(skill: Int) : BattleAI {
return null to 0.0 // we do not care about moves that dont target opponents during damage calculations
val singleTargetList = move.first.target.targetList.invoke(attacker)?.map { it as ActiveBattlePokemon }?.filter { it.battlePokemon != null }
val multiTargetList = attacker.getMultiTargetList(move.first.target)?.map { it as ActiveBattlePokemon }?.filter { it.battlePokemon != null }
if (singleTargetList != null && singleTargetList.isNotEmpty()) {
if (!singleTargetList.isNullOrEmpty()) {
// figure out target that will take the most damage
val potentialTargets = activeTracker.opponentSide.activePokemon
.filter { opp -> opp.id in singleTargetList.map { it.battlePokemon!!.uuid } }
.map { opp -> singleTargetList.first { it.battlePokemon!!.uuid == opp.id} to calculateDamage(move.first, attackerTracker, opp) }
return potentialTargets.maxBy { it.second }
} else if (multiTargetList != null && multiTargetList.isNotEmpty()) {
} else if (!multiTargetList.isNullOrEmpty()) {
val (allies, opponents) = multiTargetList.partition { it.isAllied(attacker) }
// weigh up allied damage against opponents
val totalOpponentDamage = activeTracker.opponentSide.activePokemon
@@ -188,10 +189,8 @@ class StrongBattleAI(skill: Int) : BattleAI {
// function for finding the most damaging moves in the moveset
fun mostDamagingMove(selectedMove: InBattleMove, moveset: ShowdownMoveset?, mon: TrackerPokemon, opponent: TrackerPokemon): Boolean {
if (moveset != null) {
for (move in moveset.moves.filter { !it.disabled && it.id != selectedMove.id}) {
if (calculateDamage(move, mon, opponent) > calculateDamage(selectedMove, mon, opponent)) {
return false
}
@@ -251,18 +250,19 @@ class StrongBattleAI(skill: Int) : BattleAI {
val availableMoves = moveset.moves.filter { it.canBeUsed() }.map { Pair(it, Moves.getByName(it.id)!!)}
if (!checkSkillLevel()){
val move = availableMoves
.filter { it.first.target.targetList(activeBattlePokemon)?.isEmpty() != true }
.randomOrNull()
?: return MoveActionResponse("struggle")
return chooseMove(move.first, activeBattlePokemon)
val randomMove = availableMoves.randomOrNull()
if (randomMove == null) {
if (moveset.moves.any { it.id == "struggle" })
return MoveActionResponse("struggle")
else throw IllegalActionChoiceException(activeBattlePokemon.actor,
"No valid AI move choices, but Showdown never injected Struggle!")
}
if (!checkSkillLevel()) return chooseMove(randomMove.first, activeBattlePokemon)
// switch out based on current matchup on the field
if (checkSwitchOutSkill() && shouldSwitchOut(aiSide, battle, activeBattlePokemon, moveset)) {
considerSwitching(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, availableSwitches, battle, aiSide)
considerSwitching(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, availableSwitches, battle, aiSide)?.let { return it }
}
// If HP is below 30% and not switching out, always use the most damaging move
@@ -358,7 +358,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
}
}
considerAntiBoost(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves)
considerAntiBoost(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves)?.let { return it }
// Court Change
for (move in availableMoves) {
@@ -414,7 +414,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
}
}
considerInflictingStatus(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, battle, aiSide)
considerInflictingStatus(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, battle, aiSide)?.let { return it }
// Protect style moves
for (move in availableMoves) {
@@ -430,7 +430,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
}
}
findAndUseMostDamagingMove(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, moveset, battle, aiSide)
findAndUseMostDamagingMove(activeBattlePokemon, activeTrackerPokemon, opponents, availableMoves, moveset, battle, aiSide)?.let { return it }
}
// healing wish (dealing with it here because you'd only use it if you should switch out anyway)
@@ -450,13 +450,7 @@ class StrongBattleAI(skill: Int) : BattleAI {
}
activeTrackerPokemon.firstTurn = false
val move = availableMoves
.filter { it.first.canBeUsed() }
.filter { it.first.mustBeUsed() || it.first.target.targetList(activeBattlePokemon)?.isEmpty() != true }
.randomOrNull()
?: return MoveActionResponse("struggle")
return chooseMove(move.first, activeBattlePokemon)
return chooseMove(randomMove.first, activeBattlePokemon)
}
// estimate mid-battle switch in value
@@ -978,12 +972,12 @@ class StrongBattleAI(skill: Int) : BattleAI {
val bestMove = moveValues.maxBy { it.value.second }
val target = if (bestMove.key.first.mustBeUsed()) null else bestMove.value.first
return MoveActionResponse(bestMove.key.first.id, target?.getPNX())
return chooseMove(bestMove.key.first, activeBattlePokemon, target)
}
private fun chooseMove(move: InBattleMove, activeBattlePokemon: ActiveBattlePokemon, target: ActiveBattlePokemon? = null): MoveActionResponse {
val targets = if (move.mustBeUsed()) null else move.target.targetList(activeBattlePokemon)
if (targets == null)
if (targets.isNullOrEmpty())
return MoveActionResponse(move.id)
else {
val chosenTarget = target ?: targets.filter { !it.isAllied(activeBattlePokemon) }.randomOrNull() ?: targets.random()
@@ -22,8 +22,8 @@ import com.cobblemon.mod.common.util.battleLang
* @author Hunter
* @since September 25th, 2022
*/
class FailInstruction(val message: BattleMessage): InterpreterInstruction {
class FailInstruction(val battle: PokemonBattle, val message: BattleMessage): InterpreterInstruction {
val target = message.battlePokemon(0, battle)
override fun invoke(battle: PokemonBattle) {
battle.dispatchWaiting(1.5F){
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
@@ -100,6 +100,8 @@ class MoveInstruction(
battleLang("used_move", pokemonName, move.displayName)
}
battle.broadcastChatMessage(lang)
// Notify the actor that one of their Pokemon used a move so they can react (eg. NPC play animation)
userPokemon.actor.onUseMove(userPokemon, targetPokemon)
battle.majorBattleActions[userPokemon.uuid] = message
val runtime = MoLangRuntime().also {
@@ -135,6 +137,8 @@ class MoveInstruction(
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
val failed = subsequentInstructions.any { it is FailInstruction }
val failedTargets = subsequentInstructions.filterIsInstance<FailInstruction>().mapNotNull { it.target }
val hitCountInstruction = subsequentInstructions.filterIsInstance<HitCountInstruction>().firstOrNull()
runtime.environment.query.addFunction("missed") { params ->
@@ -146,6 +150,15 @@ class MoveInstruction(
}
}
runtime.environment.query.addFunction("failed") { params ->
if (params.params.size == 0) {
return@addFunction DoubleValue(failed)
} else {
val entityUUID = params.getString(0)
return@addFunction DoubleValue(failedTargets.any { it.entity?.stringUUID == entityUUID })
}
}
val hurtTargets = subsequentInstructions.filterIsInstance<DamageInstruction>().mapNotNull { it.expectedTarget }
runtime.environment.query.addFunction("hurt") { params ->
if (params.params.size == 0) {
@@ -66,6 +66,7 @@ class GraalShowdownService : ShowdownService {
.allowArrayAccess(true)
.allowListAccess(true)
.allowMapAccess(true)
.allowAccessAnnotatedBy(Export::class.java)
.build()
context = Context.newBuilder("js")
.allowIO(true)
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.farming.ApricornHarvestEvent
@@ -176,7 +177,7 @@ class ApricornBlock(settings: Properties, val apricorn: Apricorn) : HorizontalDi
world.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(player, resetState))
if (!world.isClientSide) {
world.playSoundServer(position = pos.toVec3d(), sound = SoundEvents.ITEM_PICKUP, volume = 0.7F, pitch = 1.4F)
world.playSoundServer(position = pos.toVec3d(), sound = CobblemonSounds.APRICORN_HARVEST, volume = 1.0F, pitch = 1.0F)
if (world is ServerLevel && player is ServerPlayer) {
CobblemonEvents.APRICORN_HARVESTED.post(ApricornHarvestEvent(player, apricorn, world, pos))
@@ -203,6 +203,7 @@ class BerryBlock(private val berryIdentifier: ResourceLocation, settings: Proper
): InteractionResult {
val treeEntity = world.getBlockEntity(pos) as BerryBlockEntity
if (player.getItemInHand(InteractionHand.MAIN_HAND).item is ShovelItem && getMulch(treeEntity) != MulchVariant.NONE) {
treeEntity.setMulch(MulchVariant.NONE, world, state, pos)
treeEntity.setChanged()
world.playSound(null, pos, CobblemonSounds.MULCH_REMOVE, SoundSource.BLOCKS, 0.6F, 1F)
this.spawnDestroyParticles(world, player, pos, state.setValue(AGE, 0))
@@ -8,7 +8,7 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.block.entity.TMShelfBlockEntity
import com.cobblemon.mod.common.block.entity.DiscShelfBlockEntity
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
@@ -24,37 +24,29 @@ import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import net.minecraft.world.level.block.state.properties.DirectionProperty
import net.minecraft.world.Containers
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.shapes.VoxelShape
class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
class DiscShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
companion object {
val CODEC: MapCodec<TMShelfBlock> = simpleCodec(::TMShelfBlock)
val CODEC: MapCodec<DiscShelfBlock> = simpleCodec(::DiscShelfBlock)
val FACING: DirectionProperty = HorizontalDirectionalBlock.FACING
val SLOT_OCCUPIED_PROPERTIES = (0 until 14).map {
BooleanProperty.create("slot_${it}_occupied")
}
}
init {
val baseState = stateDefinition.any().setValue(FACING, Direction.NORTH)
registerDefaultState(SLOT_OCCUPIED_PROPERTIES.fold(baseState) { state, prop ->
state.setValue(prop, false)
})
registerDefaultState(stateDefinition.any().setValue(FACING, Direction.NORTH))
}
override fun codec() = CODEC
override fun newBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return TMShelfBlockEntity(pos, state)
return DiscShelfBlockEntity(pos, state)
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
builder.add(FACING)
SLOT_OCCUPIED_PROPERTIES.forEach { builder.add(it) }
}
override fun getRenderShape(state: BlockState): RenderShape = RenderShape.MODEL
@@ -67,7 +59,7 @@ class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
state: BlockState, level: Level, pos: BlockPos,
player: Player, hit: BlockHitResult
): InteractionResult {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return InteractionResult.PASS
val entity = level.getBlockEntity(pos) as? DiscShelfBlockEntity ?: return InteractionResult.PASS
return entity.handleUseWithoutItem(state, level, pos, player, hit)
}
@@ -75,21 +67,21 @@ class TMShelfBlock(properties: Properties) : BaseEntityBlock(properties) {
stack: ItemStack, state: BlockState, level: Level, pos: BlockPos,
player: Player, hand: InteractionHand, hit: BlockHitResult
): ItemInteractionResult {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
val entity = level.getBlockEntity(pos) as? DiscShelfBlockEntity ?: return ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
return entity.handleUseItem(stack, state, level, pos, player, hit)
}
override fun hasAnalogOutputSignal(state: BlockState) = true
override fun getAnalogOutputSignal(state: BlockState, level: Level, pos: BlockPos): Int {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
val entity = level.getBlockEntity(pos) as? DiscShelfBlockEntity
return entity?.items?.count { !it.isEmpty }?.coerceIn(0, 14) ?: 0
}
override fun onRemove(state: BlockState, level: Level, pos: BlockPos, newState: BlockState, movedByPiston: Boolean) {
if (!state.`is`(newState.block)) {
if (!level.isClientSide && !movedByPiston) {
val entity = level.getBlockEntity(pos) as? TMShelfBlockEntity
val entity = level.getBlockEntity(pos) as? DiscShelfBlockEntity
if (entity != null) {
for (index in entity.items.indices) {
val stack = entity.items[index]
@@ -127,7 +127,7 @@ class LecternBlock(properties: Properties): BaseEntityBlock(properties) {
else {
blockEntity.incrementViewerCount()
PokedexUIPacket(type = (blockEntity.getItemStack().item as PokedexItem).type, blockPos = blockPos).sendToPlayer(player as ServerPlayer)
level.playSoundServer(position = blockPos.toVec3d(), sound = CobblemonSounds.POKEDEX_OPEN, volume = 0.25F)
level.playSoundServer(position = blockPos.bottomCenter, sound = CobblemonSounds.POKEDEX_OPEN, volume = 0.25F)
}
}
}
@@ -287,7 +287,7 @@ class PCBlock(properties: Properties): BaseEntityBlock(properties), SimpleWaterl
PCLinkManager.addLink(ProximityPCLink(pc, player.uuid, baseEntity))
OpenPCPacket(pc).sendToPlayer(player)
world.playSoundServer(
position = blockPos.toVec3d(),
position = blockPos.bottomCenter,
sound = CobblemonSounds.PC_ON,
volume = 0.5F,
pitch = 1F
@@ -290,7 +290,7 @@ class PastureBlock(settings: Properties): BaseEntityBlock(settings), SimpleWater
PastureLinkManager.createLink(player.uuid, PastureLink(linkId, pcId, ResourceLocation.tryParse(world.dimensionTypeRegistration().registeredName)!!, getBasePosition(state, pos), perms))
world.playSoundServer(
position = pos.toVec3d(),
position = pos.bottomCenter,
sound = CobblemonSounds.PC_ON,
volume = 0.5F,
pitch = 1F
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.block
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.util.giveOrDropItemStack
import com.cobblemon.mod.common.util.isServerSide
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
@@ -285,7 +286,7 @@ class SaccharineLeafBlock(settings: Properties) : LeavesBlock(settings) {
// Replace the consumed empty bottle with a honey bottle
player.setItemInHand(hand, ItemStack(Items.HONEY_BOTTLE));
} else {
player.addItem(Items.HONEY_BOTTLE.defaultInstance)
player.giveOrDropItemStack(Items.HONEY_BOTTLE.defaultInstance)
}
level.playSound(null, pos, SoundEvents.BOTTLE_FILL, SoundSource.BLOCKS)
@@ -15,6 +15,7 @@ import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.component.DataComponents
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
@@ -64,7 +65,6 @@ class TypeGemClusterBlock(
val FACING: DirectionProperty = DirectionalBlock.FACING
val STAGE: IntegerProperty = IntegerProperty.create("stage", 0, 3)
val STUNTED: BooleanProperty = BooleanProperty.create("stunted")
private val DIRECTIONS = Direction.entries.toTypedArray()
fun clusterFromGemBlock(gemBlock: Block): TypeGemClusterBlock? {
return gemToClusterMap[gemBlock]
@@ -94,20 +94,20 @@ class TypeGemClusterBlock(
.setValue(STUNTED, false)
}
override fun isRandomlyTicking(state: BlockState): Boolean = state.getValue(SHOULD_GROW)
public override fun isRandomlyTicking(state: BlockState): Boolean = state.getValue(SHOULD_GROW)
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
advanceGrowth(state, level, pos, random)
}
fun advanceGrowth(state: BlockState, level: LevelAccessor, pos: BlockPos, random: RandomSource) {
if (!state.getValue(SHOULD_GROW)) return
if (!gemClusterCanGrow(state, level, pos)) return
val currentStage = state.getValue(STAGE)
if (currentStage < 3) {
// Progress through stages regardless of STUNTED
level.setBlock(pos, state.setValue(STAGE, currentStage + 1), UPDATE_CLIENTS)
level.setBlock(pos, state.setValue(STAGE, currentStage + 1), UPDATE_ALL)
return
}
@@ -116,32 +116,31 @@ class TypeGemClusterBlock(
val facing = state.getValue(FACING)
if (isStunted) {
level.setBlock(pos, state.setValue(SHOULD_GROW, false), UPDATE_CLIENTS)
level.setBlock(pos, state.setValue(SHOULD_GROW, false), UPDATE_ALL)
return
}
// Check neighboring gem blocks
var hasConflict = false
for (dir in DIRECTIONS) {
if (dir == facing.opposite) continue
if (isGem(level.getBlockState(pos.relative(dir)))) {
hasConflict = true
break
}
}
val hasConflict = Direction.entries
.filter { it != facing.opposite }
.any { dir ->
val neighborPos = pos.relative(dir)
val neighborState = level.getBlockState(neighborPos)
isGem(neighborState)
}
if (hasConflict) {
level.setBlock(
pos,
state.setValue(STUNTED, true).setValue(SHOULD_GROW, false),
UPDATE_CLIENTS
UPDATE_ALL
)
} else {
var nextState = nextStage.defaultBlockState()
if (nextState.hasProperty(FACING)) {
nextState = nextState.setValue(FACING, facing)
}
level.setBlock(pos, nextState, UPDATE_CLIENTS)
level.setBlock(pos, nextState, UPDATE_ALL)
attachDecorativeClusters(level, pos, random)
}
}
@@ -151,28 +150,22 @@ class TypeGemClusterBlock(
}
private fun attachDecorativeClusters(level: LevelAccessor, gemPos: BlockPos, random: RandomSource) {
val openDirections = ArrayList<Direction>(DIRECTIONS.size)
for (direction in DIRECTIONS) {
if (level.getBlockState(gemPos.relative(direction)).isAir) {
openDirections.add(direction)
}
}
val openDirections = Direction.entries.filter { direction ->
level.getBlockState(gemPos.relative(direction)).isAir
}.toMutableList()
if (openDirections.isEmpty()) {
return
}
var remaining = openDirections.size
val minClusters = maxOf(1, (remaining * 0.75f).toInt())
val clusterCount = random.nextInt(minClusters, remaining + 1)
val minClusters = maxOf(1, (openDirections.size * 0.75f).toInt())
val clusterCount = random.nextInt(minClusters, openDirections.size + 1)
repeat(clusterCount) {
if (remaining == 0) return@repeat
if (openDirections.isEmpty()) return@repeat
val index = random.nextInt(remaining)
val direction = openDirections[index]
remaining--
openDirections[index] = openDirections[remaining]
val index = random.nextInt(openDirections.size)
val direction = openDirections.removeAt(index)
val clusterPos = gemPos.relative(direction)
val stage = random.nextInt(0, 4)
@@ -182,23 +175,33 @@ class TypeGemClusterBlock(
.setValue(SHOULD_GROW, false) // so we can reduce lag... hopefully
.setValue(STUNTED, false)
level.setBlock(clusterPos, clusterState, UPDATE_CLIENTS)
level.setBlock(clusterPos, clusterState, UPDATE_ALL)
}
}
fun gemClusterCanGrow(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
if (!state.getValue(SHOULD_GROW)) return false
val supportPos = pos.relative(state.getValue(FACING).opposite)
val supportBlock = level.getBlockState(supportPos).block
return CobblemonBlocks.typeGemBlocks().containsValue(supportBlock) || (supportBlock == CobblemonBlocks.TYPE_GEM_CORE) // Cannot grow if not on any gem blocks
}
override fun getDrops(state: BlockState, params: LootParams.Builder): List<ItemStack> {
val drops = super.getDrops(state, params)
for (stack in drops) {
val blockItem = stack.item as? BlockItem ?: continue
if (blockItem.block !is TypeGemClusterBlock) continue
val itemPath = BuiltInRegistries.ITEM.getKey(stack.item).path
if (!itemPath.endsWith("_gem_cluster")) continue
stack.set(
DataComponents.BLOCK_STATE,
BlockItemStateProperties.EMPTY
.with(STAGE, state.getValue(STAGE))
.with(SHOULD_GROW, false)
.with(STUNTED, state.getValue(STUNTED))
.with(STUNTED, false)
)
stack.set(
DataComponents.CUSTOM_MODEL_DATA,
@@ -253,9 +256,9 @@ class TypeGemClusterBlock(
}
override fun canSurvive(state: BlockState, level: LevelReader, pos: BlockPos): Boolean {
val supportPos = pos.relative(state.getValue(FACING).opposite)
val supportState = level.getBlockState(supportPos)
return supportState.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS) || (supportState.block == CobblemonBlocks.TYPE_GEM_CORE)
val direction = state.getValue(FACING)
val supportPos = pos.relative(direction.opposite)
return level.getBlockState(supportPos).isFaceSturdy(level, supportPos, direction)
}
override fun propagatesSkylightDown(state: BlockState, level: net.minecraft.world.level.BlockGetter, pos: BlockPos): Boolean = true
@@ -271,4 +274,23 @@ class TypeGemClusterBlock(
super.createBlockStateDefinition(builder)
builder.add(FACING, SHOULD_GROW, STAGE, STUNTED)
}
override fun getCloneItemStack(level: LevelReader, pos: BlockPos, state: BlockState): ItemStack {
val stack = super.getCloneItemStack(level, pos, state)
val itemPath = BuiltInRegistries.ITEM.getKey(stack.item).path
if (itemPath.endsWith("_gem_cluster")) {
stack.set(
DataComponents.BLOCK_STATE,
BlockItemStateProperties.EMPTY
.with(STAGE, state.getValue(STAGE))
.with(SHOULD_GROW, false)
.with(STUNTED, false)
)
stack.set(
DataComponents.CUSTOM_MODEL_DATA,
CustomModelData(state.getValue(STAGE))
)
}
return stack
}
}
@@ -8,22 +8,22 @@
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.api.tags.CobblemonBlockTags
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.SHOULD_GROW
import com.cobblemon.mod.common.block.TypeGemClusterBlock.Companion.STUNTED
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.util.RandomSource
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.LevelAccessor
import net.minecraft.world.level.WorldGenLevel
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.DirectionalBlock
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.block.state.StateDefinition
import net.minecraft.world.level.block.state.properties.BooleanProperty
import java.util.*
class TypeGemCoreBlock(properties: Properties) : Block(properties) {
@@ -32,14 +32,33 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
const val MAX_CONNECTED_GEMS = 7
const val MIN_DISTANCE_BETWEEN_GEMS = 1
const val CONTINUATION_CHANCE = 0.8f
const val INACTIVE_WAKE_RECHECK_CHANCE = 10
val ACTIVE: BooleanProperty = BooleanProperty.create("active")
private val DIRECTIONS = Direction.entries.toTypedArray()
val BLOCK_TO_CLUSTER: Map<ResourceLocation, Block> by lazy {
mapOf(
ResourceLocation.parse("cobblemon:normal_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_NORMAL,
ResourceLocation.parse("cobblemon:fire_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIRE,
ResourceLocation.parse("cobblemon:water_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_WATER,
ResourceLocation.parse("cobblemon:electric_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_ELECTRIC,
ResourceLocation.parse("cobblemon:grass_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_GRASS,
ResourceLocation.parse("cobblemon:ice_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_ICE,
ResourceLocation.parse("cobblemon:fighting_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_FIGHTING,
ResourceLocation.parse("cobblemon:poison_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_POISON,
ResourceLocation.parse("cobblemon:ground_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_GROUND,
ResourceLocation.parse("cobblemon:flying_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_FLYING,
ResourceLocation.parse("cobblemon:psychic_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_PSYCHIC,
ResourceLocation.parse("cobblemon:bug_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_BUG,
ResourceLocation.parse("cobblemon:rock_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_ROCK,
ResourceLocation.parse("cobblemon:ghost_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_GHOST,
ResourceLocation.parse("cobblemon:dragon_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
ResourceLocation.parse("cobblemon:dark_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
ResourceLocation.parse("cobblemon:steel_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
ResourceLocation.parse("cobblemon:fairy_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY
)
}
fun setStuntState(level: LevelAccessor, gemBlocks: List<Pair<BlockState, BlockPos>>, stunted: Boolean) {
for ((_, gemPos) in gemBlocks) {
for (dir in DIRECTIONS) {
for (dir in Direction.entries) {
val neighborPos = gemPos.relative(dir)
val neighborState = level.getBlockState(neighborPos)
@@ -52,7 +71,7 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
updatedState = updatedState.setValue(SHOULD_GROW, !stunted)
}
level.setBlock(neighborPos, updatedState, UPDATE_CLIENTS)
level.setBlock(neighborPos, updatedState, 3)
}
}
}
@@ -71,7 +90,7 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
while (queue.isNotEmpty()) {
val current = queue.poll()
for (direction in DIRECTIONS) {
for (direction in Direction.entries) {
val neighbor = current.relative(direction)
if (visited.add(neighbor)) {
val state = level.getBlockState(neighbor)
@@ -86,26 +105,8 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
}
}
init {
registerDefaultState(stateDefinition.any().setValue(ACTIVE, true))
}
override fun randomTick(state: BlockState, level: ServerLevel, pos: BlockPos, random: RandomSource) {
if (!state.getValue(ACTIVE)) {
if (random.nextInt(INACTIVE_WAKE_RECHECK_CHANCE) != 0) {
return
}
if (shouldWakeFromSleep(level, pos)) {
level.setBlock(pos, state.setValue(ACTIVE, true), UPDATE_CLIENTS)
}
return
}
val result = grow(level, pos, random)
if (!result.grew && !result.shouldKeepTicking) {
level.setBlock(pos, state.setValue(ACTIVE, false), UPDATE_CLIENTS)
}
grow(level, pos, random)
}
fun forceGrow(level: WorldGenLevel, pos: BlockPos, random: RandomSource, percentage: Float) {
@@ -113,21 +114,19 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
while (true) {
val growInfo = grow(level, pos, random, true)
val grown = growInfo.grew
val clusterSize = growInfo.clusterSize
val grown = growInfo.first
val clusterSize = growInfo.second
if (!grown || clusterSize >= desiredLength) break
}
}
private data class GrowResult(val grew: Boolean, val clusterSize: Int, val shouldKeepTicking: Boolean)
private fun grow(
level: WorldGenLevel,
pos: BlockPos,
random: RandomSource,
forced: Boolean = false
): GrowResult {
): Pair<Boolean, Int> {
val connectedGems = getConnectedGemBlocks(level, pos) // maybe we can store this and access it for better performance? check for changes in list rather than checking each time grow is called? idk
val gemCount = connectedGems.size
val overLimit = gemCount >= MAX_CONNECTED_GEMS
@@ -135,24 +134,23 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
if (overLimit) {
// if there is no open air pockets to grow any new clusters then exit early
if (!hasBreathingRoom(level, connectedGems)) {
return GrowResult(false, gemCount, false)
return Pair(false, gemCount)
}
// max type gem block count met, tell clusters to grow but not turn into blocks
setStuntState(level, connectedGems, true)
return GrowResult(false, gemCount, false)
//return Pair(false, gemCount)
} else {
// make sure connected clusters know they can grow and turn into blocks
setStuntState(level, connectedGems, false)
}
val gemStart = random.nextInt(gemCount)
for (gemOffset in 0 until gemCount) {
val (gemState, gemPos) = connectedGems[(gemStart + gemOffset) % gemCount]
val clusterBlock = TypeGemClusterBlock.clusterFromGemBlock(gemState.block)
val directionStart = random.nextInt(DIRECTIONS.size)
val javaRandom = java.util.Random(random.nextLong())
for (dirOffset in DIRECTIONS.indices) {
val dir = DIRECTIONS[(directionStart + dirOffset) % DIRECTIONS.size]
for ((gemState, gemPos) in connectedGems.shuffled(javaRandom)) {
val registryKey = BuiltInRegistries.BLOCK.getKey(gemState.block)
val clusterBlock = BLOCK_TO_CLUSTER[registryKey] ?: continue // If there is no block to grow, skip the block
for (dir in Direction.entries.shuffled(javaRandom)) {
val targetPos = gemPos.relative(dir)
val targetState = level.getBlockState(targetPos)
@@ -160,26 +158,24 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
if (!isPositionValidForGrowth(level, targetPos)) continue
// Place gem cluster
if (clusterBlock != null) {
var placeState = clusterBlock.defaultBlockState()
.setValue(DirectionalBlock.FACING, dir)
.setValue(TypeGemClusterBlock.STAGE, 0)
.setValue(SHOULD_GROW, true)
val placeState = clusterBlock.defaultBlockState()
.setValue(DirectionalBlock.FACING, dir)
.setValue(TypeGemClusterBlock.STAGE, 0)
.setValue(SHOULD_GROW, true)
level.setBlock(targetPos, placeState, UPDATE_CLIENTS)
}
level.setBlock(targetPos, placeState, UPDATE_ALL)
if (forced && clusterBlock != null) {
if (forced) {
forceAdvanceClusterGrowth(level, targetPos, random)
}
val updatedGemCount = if (forced) getConnectedGemBlocks(level, pos).size else connectedGems.size
return GrowResult(true, updatedGemCount, true)
return Pair(true, updatedGemCount)
}
}
// println("[TypeGemCoreBlock] No valid spot found for gem placement.")
return GrowResult(false, connectedGems.size, false)
return Pair(false, connectedGems.size)
}
private fun forceAdvanceClusterGrowth(level: WorldGenLevel, clusterPos: BlockPos, random: RandomSource) {
@@ -190,18 +186,9 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
}
}
private fun shouldWakeFromSleep(level: ServerLevel, pos: BlockPos): Boolean {
val connectedGems = getConnectedGemBlocks(level, pos)
if (connectedGems.size >= MAX_CONNECTED_GEMS) {
return false
}
return hasBreathingRoom(level, connectedGems)
}
private fun hasBreathingRoom(level: WorldGenLevel, gems: List<Pair<BlockState, BlockPos>>): Boolean {
for ((_, gemPos) in gems) {
for (dir in DIRECTIONS) {
for (dir in Direction.entries) {
if (level.getBlockState(gemPos.relative(dir)).isAir) {
return true
}
@@ -211,7 +198,7 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
}
private fun isPositionValidForGrowth(level: WorldGenLevel, pos: BlockPos): Boolean {
return DIRECTIONS.none { dir ->
return Direction.entries.none { dir ->
val nearby = pos.relative(dir)
isGem(level.getBlockState(nearby)) &&
pos.distManhattan(nearby) < MIN_DISTANCE_BETWEEN_GEMS
@@ -222,17 +209,5 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
return state.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
}
override fun neighborChanged(state: BlockState, level: Level, pos: BlockPos, block: Block, fromPos: BlockPos, isMoving: Boolean) {
super.neighborChanged(state, level, pos, block, fromPos, isMoving)
if (!level.isClientSide && !state.getValue(ACTIVE)) {
level.setBlock(pos, state.setValue(ACTIVE, true), UPDATE_CLIENTS)
}
}
override fun createBlockStateDefinition(builder: StateDefinition.Builder<Block, BlockState>) {
super.createBlockStateDefinition(builder)
builder.add(ACTIVE)
}
override fun isRandomlyTicking(state: BlockState): Boolean = true
}
@@ -10,7 +10,7 @@ package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.block.TMShelfBlock
import com.cobblemon.mod.common.block.DiscShelfBlock
import com.cobblemon.mod.common.item.interactive.TechnicalMachineItem
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
@@ -36,7 +36,7 @@ import net.minecraft.world.level.gameevent.GameEvent
import java.util.OptionalInt
import kotlin.math.pow
class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.TM_SHELF, pos, state), WorldlyContainer {
class DiscShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISC_SHELF, pos, state), WorldlyContainer {
val items: NonNullList<ItemStack> = NonNullList.withSize(14, ItemStack.EMPTY)
var lastInteractedSlot: Int = -1
private val accessibleSlots = IntArray(14) { it }
@@ -56,10 +56,11 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
}
items[slot] = stack.copyWithCount(1)
stack.shrink(1)
if (!player.isCreative) {
stack.shrink(1)
}
lastInteractedSlot = slot
updateBlockState(level, pos)
markUpdated()
onInventoryChanged(level, pos)
return ItemInteractionResult.sidedSuccess(level.isClientSide)
}
@@ -75,23 +76,20 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
}
lastInteractedSlot = slot
updateBlockState(level, pos)
onInventoryChanged(level, pos)
return InteractionResult.sidedSuccess(level.isClientSide)
}
private fun updateBlockState(level: Level, pos: BlockPos) {
val newState = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.fold(blockState) { acc, prop ->
val index = TMShelfBlock.SLOT_OCCUPIED_PROPERTIES.indexOf(prop)
acc.setValue(prop, !items[index].isEmpty)
}
level.setBlock(pos, newState, 3)
level.updateNeighbourForOutputSignal(pos, newState.block)
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(newState))
private fun onInventoryChanged(level: Level, pos: BlockPos) {
setChanged()
level.updateNeighbourForOutputSignal(pos, blockState.block)
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(blockState))
level.sendBlockUpdated(pos, blockState, blockState, 3)
}
private fun getHitSlot(hit: BlockHitResult, state: BlockState): OptionalInt {
val facing = state.getValue(TMShelfBlock.FACING)
val facing = state.getValue(DiscShelfBlock.FACING)
if (hit.direction != facing) {
return OptionalInt.empty()
}
@@ -102,8 +100,8 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
// Face-local X where 0 = visual left and 1 = visual right.
Direction.NORTH -> 1.0 - relative.x to relative.y
Direction.SOUTH -> relative.x to relative.y
Direction.WEST -> 1.0 - relative.z to relative.y
Direction.EAST -> relative.z to relative.y
Direction.WEST -> relative.z to relative.y
Direction.EAST -> 1.0 - relative.z to relative.y
else -> return OptionalInt.empty()
}
@@ -175,16 +173,10 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
NOTE_SLOT_TO_SEMITONE.forEach { (slot, semitone) ->
if (items[slot].isEmpty) return@forEach
val pitch = 2.0.pow((semitone + octaveShift) / 12.0).toFloat()
level.playSound(null, noteBlockPos, soundEvent, SoundSource.BLOCKS, 3.0f, pitch)
level.playSound(null, noteBlockPos, soundEvent, SoundSource.RECORDS, 3.0f, pitch)
}
}
fun markUpdated() {
level?.setBlock(blockPos, blockState, 3)
level?.sendBlockUpdated(blockPos, blockState, blockState, 3)
setChanged()
}
override fun clearContent() {
var changed = false
for (index in items.indices) {
@@ -194,8 +186,7 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
}
}
if (changed) {
level?.let { updateBlockState(it, blockPos) }
markUpdated()
level?.let { onInventoryChanged(it, blockPos) }
}
}
@@ -214,8 +205,7 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
if (items[slot].isEmpty) {
lastInteractedSlot = slot
}
level?.let { updateBlockState(it, blockPos) }
markUpdated()
level?.let { onInventoryChanged(it, blockPos) }
}
return removed
}
@@ -223,8 +213,7 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
override fun removeItemNoUpdate(slot: Int): ItemStack {
val removed = ContainerHelper.takeItem(items, slot)
if (!removed.isEmpty) {
level?.let { updateBlockState(it, blockPos) }
markUpdated()
level?.let { onInventoryChanged(it, blockPos) }
}
return removed
}
@@ -235,8 +224,7 @@ class TMShelfBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(Cobblem
items[slot] = if (stack.isEmpty) ItemStack.EMPTY else stack.copyWithCount(1)
lastInteractedSlot = slot
level?.let { updateBlockState(it, blockPos) }
markUpdated()
level?.let { onInventoryChanged(it, blockPos) }
}
override fun stillValid(player: Player): Boolean {

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