Compare commits

...
244 Commits
Author SHA1 Message Date
Lily 4faae33d43 mod station groundwork 2024-03-06 15:47:16 -05:00
Lily bd8f761ddb Merge branch 'tm-machine' into mod_station
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/command/TestCommand.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2024-03-06 15:07:52 -05:00
Paul 46798d694e Fixed generic entities not rendering, fixed Torkoal particles going upside down, fixed idle animation particles not playing, fixed loop particles not looping 2024-03-04 11:26:20 +10:00
Hiroku 056dd0fd4b Merge branch 'JsonScriptFixes' into 'main'
tiny Json script fixes

See merge request cable-mc/cobblemon!853
2024-03-03 07:54:12 +00:00
WaldleuferandHiroku 3ac00d976b tiny Json script fixes 2024-03-03 07:54:11 +00:00
Paul b30a76855b Fixed cancelling spawn events not actually cancelling the action, closes #612 2024-03-03 17:18:08 +10:00
Paul b6c964e03b Fixed Forge runs, again 2024-03-03 14:40:18 +10:00
Paul 3ed46cb7af Removed unused option in config (maxHorizontalSpace) 2024-03-03 13:38:59 +10:00
Paul cf9efe9b8c Fixed Snowstorm particles sometimes not appearing 2024-03-03 13:28:40 +10:00
Paul 02b9431731 Attach the entity from the entity particle packet to the storm (oops) 2024-03-03 13:14:13 +10:00
Paul a30ae0ccef Added query functions for entity_width, height, size, and radius to entity tracked particle storms 2024-03-03 12:46:28 +10:00
Hiroku a2905f4d37 Merge branch 'particle-events' into 'main'
Added "event" support to bedrock particles, locator support in the command,...

Closes #767

See merge request cable-mc/cobblemon!900
2024-03-03 02:00:36 +00:00
Paul b3aba1532c Added "event" support to bedrock particles, locator support in the command, and fixed inwards motion direction 2024-03-03 11:28:54 +10:00
Hiroku 6369bdb612 Merge branch '1.5_Structures' into 'main'
Added most of the 1.5 Structures

See merge request cable-mc/cobblemon!877
2024-03-03 00:20:02 +00:00
Hiroku b5db5605ef Merge branch 'FossilAnalyzerDirRenderFix' into 'main'
Fossils are now positioned correctly in all possible block facing directions

See merge request cable-mc/cobblemon!899
2024-03-02 23:48:29 +00:00
JazzMcNade 0207459a0f Fossils are now positioned correctly in all possible block facing directions 2024-03-02 16:37:44 -06:00
Hiroku 0e2fdfbce8 Merge branch 'ability-fix' into 'main'
Abilities fix

See merge request cable-mc/cobblemon!891
2024-03-02 05:20:10 +00:00
Hiroku 8c9da885c7 Merge branch 'main' into 'ability-fix'
# Conflicts:
#   CHANGELOG.md
2024-03-02 04:16:11 +00:00
Hiroku a5b6529f3c Merge branch 'display_case' into 'main'
Display case

See merge request cable-mc/cobblemon!879
2024-03-02 04:10:44 +00:00
Paul b430b7db76 Corrected Gimmighoul chest battle behaviour on server environments 2024-03-02 12:02:35 +10:00
Hiroku 8015ff0e20 Merge branch 'fix-grow-tumblestone-advancement' into 'main'
Grow Tumblestone Advancement triggers when placed near Lava or Magma

See merge request cable-mc/cobblemon!893
2024-03-02 01:21:55 +00:00
Dhyan PatelandHiroku ee78657e3d Grow Tumblestone Advancement triggers when placed near Lava or Magma 2024-03-02 01:21:55 +00:00
Hiroku 6cc84cd38c Merge branch 'tooltip-fix-fix' into 'main'
fix a small issue with appending tooltips

See merge request cable-mc/cobblemon!897
2024-03-02 00:28:30 +00:00
miasmus 31d6ff541e fix a small issue with appending tooltips 2024-03-01 16:36:31 -05:00
jeffw773 e1a4086f24 - push new changes for Slippy 2024-02-29 20:11:09 -08:00
Paul b1992a94d3 Fixed piglin entity mixin on Forge 2024-03-01 11:10:47 +10:00
Hiroku b41c4c21a8 Merge branch 'publish-versioning-fix' into 'main'
fix versioning

See merge request cable-mc/cobblemon!894
2024-02-28 22:07:33 +00:00
miasmus e1893f50ae fix versioning 2024-02-28 16:52:13 -05:00
Lily a458b31c9f Merge branch 'main' into display_case
# Conflicts:
#	.gitignore
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/battle/subscreen/BattleGeneralActionSelection.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/models/item/relic_coin_pouch.json
#	common/src/main/resources/assets/cobblemon/sounds.json
2024-02-28 14:07:19 -05:00
Lily 03b5336eb8 refactor getPositioningType to avoid using tags 2024-02-28 14:02:29 -05:00
Apion a270371632 Merge branch 'sturdier_tumblestones' into 'main'
fix tumblestone blocks being breakable without a pickaxe

See merge request cable-mc/cobblemon!892
2024-02-28 18:23:21 +00:00
LilyandApion a9caf92d2f fix tumblestone blocks being breakable without a pickaxe 2024-02-28 18:23:21 +00:00
Apion ff4d9ae208 Merge branch 'gimmighoul_gholdengo' into 'main'
Add Gimmighoul, Gholdengo, Relic Coins and Gilded Chests

See merge request cable-mc/cobblemon!888
2024-02-28 02:03:37 +00:00
LilyandApion a87d522f6d Add Gimmighoul, Gholdengo, Relic Coins and Gilded Chests 2024-02-28 02:03:37 +00:00
Licious c24ab2049d Merge branch 'main' into ability-fix
# Conflicts:
#	CHANGELOG.md
2024-02-26 11:45:32 +00:00
Licious b7b1850a4e Clarify changelog. 2024-02-26 11:41:49 +00:00
Licious 8995c107f7 Fix abilities, add ability capsule & patch. 2024-02-26 11:38:22 +00:00
Hiroku dca6b5273d Merge branch 'FossilAssetUpdate2' into 'main'
New animation assets for fetuses

See merge request cable-mc/cobblemon!889
2024-02-26 07:57:15 +00:00
JazzMcNade 2e8483cdc7 Corrected comment, fixed machine not dropping fossils when destroyed while not started. 2024-02-26 01:29:11 -06:00
Hiroku 6b1c2e2074 Merge branch 'showdown_gen9dlc' into 'main'
Update Showdown with Gen9 DLC.

See merge request cable-mc/cobblemon!890
2024-02-26 03:52:34 +00:00
Segfault Guy 8af5b5e07a Update Showdown with Gen9 DLC. 2024-02-25 22:44:27 -05:00
Hiroku 7ae19ca774 Merge branch 'issue-763' into 'main'
Stop Rails from being Spawnable tiles

Closes #763

See merge request cable-mc/cobblemon!881
2024-02-26 03:13:27 +00:00
Dhyan PatelandHiroku 830f00553e Stop Rails from being Spawnable tiles 2024-02-26 03:13:27 +00:00
Hiroku c87f56c83f Merge branch 'lang-fixes' into 'main'
brought a couple inconsistent lang keys into consistency

See merge request cable-mc/cobblemon!887
2024-02-25 02:06:17 +00:00
tmetcalfe89 4478532bcb brought a couple inconsistent lang keys into consistency 2024-02-24 18:02:02 -08:00
Hiroku 184348d6fa Merge branch 'lazy-psf' into 'main'
made the spawns lazy so we can ref the object's other properties without violating the lateinit

Closes #765

See merge request cable-mc/cobblemon!886
2024-02-23 23:31:03 +00:00
Hiroku 04d288ed7e Merge branch 'issue-760' into 'main'
Fix Medicinal Brews / Potions Stacking in Brewing Stand

See merge request cable-mc/cobblemon!876
2024-02-23 23:29:52 +00:00
Dhyan PatelandHiroku 0d5cce730e Fix Medicinal Brews / Potions Stacking in Brewing Stand 2024-02-23 23:29:51 +00:00
tmetcalfe89 3e01535203 license 2024-02-23 12:03:45 -08:00
tmetcalfe89 f0d99a9d83 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into lazy-psf 2024-02-23 11:42:40 -08:00
tmetcalfe89 b4eb46d632 it's lazy, but it can change 2024-02-23 11:42:35 -08:00
Hiroku 7e52336255 Merge branch 'InconsistentHpBarFix' into 'main'
InconsistentHpBarFix

Closes #761

See merge request cable-mc/cobblemon!875
2024-02-23 02:39:42 +00:00
Hiroku 2b40b5b689 Merge branch 'feather-dusting' into 'main'
Feathers

See merge request cable-mc/cobblemon!882
2024-02-23 02:39:01 +00:00
tmetcalfe89 da889f220b made the spawns lazy so we can ref the object's other properties without violating the lateinit 2024-02-22 14:55:29 -08:00
Lily ac65910380 updated the relic coin pouch rendering inside display case 2024-02-22 17:18:20 -05:00
Lily ba6847be9a Merge branch 'gimmighoul_gholdengo' into display_case
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
2024-02-22 16:27:39 -05:00
Lily 5ddbcd1830 various changes to display case 2024-02-22 15:03:17 -05:00
Lily 47605646d0 Merge branch 'main' into display_case 2024-02-22 14:26:33 -05:00
Lily d9fdf089c2 fix display case z-fighting 2024-02-22 14:00:15 -05:00
dhi_holo 41db2a53ce Update en_us.json
just adding the feather tag to the translations so EMI stops complaining about it (and fixing a typo)
2024-02-22 11:28:58 +10:00
dhi_holo 85d8779818 The Sound of... Feathers?
it seems to be working correctly, so let's ship it out
2024-02-22 11:27:54 +10:00
Lily e9a405694c display case sounds 2024-02-21 18:47:20 -05:00
Licious d5af099120 Merge branch 'main' into tm-machine 2024-02-21 20:34:34 +00:00
Hiroku d817ce1471 Merge branch 'gui-mod-compat-fixes' into 'main'
Client screen crash fixes with other mods

See merge request cable-mc/cobblemon!885
2024-02-21 11:44:38 +00:00
LiciousandHiroku ce09b66db5 Client screen crash fixes with other mods 2024-02-21 11:44:38 +00:00
Hiroku d0c666a20d Merge branch 'main' into 'feather-dusting'
# Conflicts:
#   CHANGELOG.md
2024-02-21 10:03:38 +00:00
Hiroku 04cef915e4 Merge branch 'tagging-youre-it' into 'main'
Big Tags

See merge request cable-mc/cobblemon!884
2024-02-21 09:50:44 +00:00
dhi_holo e50412443e Update CHANGELOG.md
Removed weird incorrect text from Type Gem changelog entry, mentioned additions from this branch.
2024-02-21 19:11:24 +10:00
dhi_holo 3fe1f87f18 apricorn trapdoor now doesn't look jank when opening
just changed the blockstate from the oak trapdoor style to the spruce trapdoor style, so the pokeball direction stays consistent when opened or closed
2024-02-21 19:08:38 +10:00
dhi_holo 5a0fc199ff Create feathers.json
item tag, since I forgot that would be smart to have
2024-02-21 17:35:07 +10:00
dhi_holo beb769c2a5 Update CobblemonItemGroups.kt
small tweak to how the items are placed for gaurenteed consistency
2024-02-21 17:31:29 +10:00
dhi_holo a7b6fae4ed gems, fang, claw, and armors get tagged
Type Gems are now considered held items, and four evolution items are considered evolution items (by tags)
2024-02-21 17:28:00 +10:00
dhi_holo af2ff7bbfa Gems
Type Gems given an item tag, and moved from Evolution Items to Archeology and Held Items
2024-02-21 17:11:17 +10:00
dhi_holo 4f3575b2c6 Poke Ball Materials and Bud Item Tag
adds the tumblestone clusters and buds to the item tags, and adds fire stone ore to each ore tag, but primarily

Redoes every Poké Ball recipe (besides Master) to use tags to handle the tier material, allowing datapacks to easily add alternate recipes besides Copper, Iron, Gold, or Diamonds
2024-02-21 16:41:41 +10:00
dhi_holo 98c248ef13 corrections
renaming some stuff to be prepared for the next step of this insane idea
2024-02-21 15:34:20 +10:00
dhi_holo 4a2c69d47c Tags on top of Tags
Added a very large amount of tags.
The different tiers of Poké balls, also the Apricorn balls so it's easier for people to see what balls count for that advancement, different healing and battle item types, some evolution item sets which made sense to make tags (may add more later, these ones were just the obvious ones to make thing easier for future), the three colours for tumblestones, machine blocks, as well as adding the fence gate item tag, and adding the new tumblestone stuff to the pickaxe tag (although they can still be mined with an axe, probably something that needs fixing)

Mod loader tags were not tested on Forge, due to a crash involving CobblemonEntities, but Fabric works all nicely
2024-02-21 15:29:55 +10:00
dhi_holo c23cb644d9 Update CHANGELOG.md
grr evil file
2024-02-21 09:43:33 +10:00
dhi_holo a8b12cfca3 Feathers
Adds the six EV Feathers, hopefully they work first try :)
2024-02-21 09:32:05 +10:00
Lily 825e1ee110 Merge branch 'type_gem' into tm-machine
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
2024-02-20 17:54:13 -05:00
Apion b489e747bb Merge branch 'type_gem' into 'main'
added type gems

See merge request cable-mc/cobblemon!880
2024-02-20 13:22:41 +00:00
Lily 08f957e33b Update CHANGELOG.md 2024-02-19 21:32:30 -05:00
Lily e17d38c53a added type gems 2024-02-19 21:24:29 -05:00
Lily ee0dc24207 temporarily removed type gems 2024-02-19 20:52:26 -05:00
Lily e5d702bb5a added new type gem sprites 2024-02-19 20:41:41 -05:00
apion 0397e39735 I have become the very thing I swore to destroy (Pushing a license to main D:) 2024-02-19 20:18:55 -05:00
Apion 6ca1d23f45 Merge branch 'no_more_pick_block' into 'main'
disallow pick-stacking blocks that are part of a multiblock structure to prevent weird issues with NBT copying

See merge request cable-mc/cobblemon!874
2024-02-19 23:38:26 +00:00
JazzMcNade 44def2be76 Fossil is returned if the machine is broken before completion. Restoration Block now uses glass sounds. Mobs no longer attempt to pathfind through the restoration tank. 2024-02-17 20:37:05 -06:00
JazzMcNade 507f70c2d5 Fixed tank missing blockstates, fixed hoppers inserting into tank 2024-02-17 18:46:02 -06:00
JazzMcNade d91118d05f Updated helix fossil item image 2024-02-17 14:17:34 -06:00
JazzMcNade 07d8e6ed09 Adjusted fetus offset and scaling 2024-02-16 21:20:09 -06:00
Lily fb34d4bd23 suppressed gimmighoul's desire to be a necromancer 2024-02-16 20:50:03 -05:00
Paul 6ca1337ff9 Merge branch 'gimmighoul_gholdengo' into 1.5_Structures 2024-02-17 10:29:18 +10:00
Lily 0d344337dd Merge branch 'main' into display_case
# Conflicts:
#	.gitignore
#	CHANGELOG.md
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
2024-02-16 11:55:15 -05:00
Lily c6e514216c added new obtain methods 2024-02-16 11:26:36 -05:00
Hiroku 64d160020b Merge branch 'issue-745' into 'main'
Sobble Line Can Swim and Breathe Underwater

Closes #745

See merge request cable-mc/cobblemon!878
2024-02-16 06:36:50 +00:00
Dhyan PatelandHiroku fb4c80f68c Sobble Line Can Swim and Breathe Underwater 2024-02-16 06:36:50 +00:00
Paul 3d27681aaa Randomize Pokémon spawn positions and position gimmighoul reveals better 2024-02-16 16:31:00 +10:00
Paul 2b5433f9da Added hit effect for pokemon receiving battle damage 2024-02-16 15:37:52 +10:00
Paul 352202203a Merge branch 'gimmighoul_gholdengo' into 1.5_Structures 2024-02-16 15:04:51 +10:00
Paul 62cfbd7087 Licenses 2024-02-16 15:03:53 +10:00
Paul 5a64e8ea1b Animated the trap chest opening 2024-02-16 15:03:15 +10:00
Paul 8dc9863a5d Conflict resolution and cleanup 2024-02-16 14:39:22 +10:00
JazzMcNade 9bad141c3f Added remaining fetus animations, updated embryo animations 2024-02-15 21:57:42 -06:00
Myslippy 9280258413 Implemented most of the Fossil & Ruin Structures 2024-02-16 03:13:32 +00:00
Paul 51a11163dd Merge remote-tracking branch 'origin/main' into gimmighoul_gholdengo
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonBlocks.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/block/FossilTubeRenderer.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/bedrock/animation/BedrockStatefulAnimation.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Species.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
2024-02-16 12:43:35 +10:00
Paul fe8e4dcaff Improved Pokémon pathfinding so they don't run through cactus and such so recklessly 2024-02-16 12:24:58 +10:00
Paul e0bd6766fc Fixed basculin stripe spawning 2024-02-16 11:37:16 +10:00
Paul 4da800d396 Fixed Arbok patterns not being used properly in spawn files 2024-02-16 11:19:24 +10:00
JazzMcNade f3246ae14a Updated fossil sprites, refactored Galar fossil names 2024-02-15 01:33:11 -06:00
Lily d5ad84a495 refactored PositioningType 2024-02-13 19:02:53 -05:00
JazzMcNade 47f87b1be3 Rearranged pokemon creation so that currentHealth is assigned after ivs and evs are set 2024-02-13 00:08:04 -06:00
Lily b01086270a fix positioning on certain items that use block entity renderers 2024-02-12 19:18:18 -05:00
Lily da6a09c942 disallow pick-stacking blocks that are part of a multiblock structure to prevent weird issues with NBT copying 2024-02-12 17:40:39 -05:00
Lily 8d5eb5883d gilded chests now get blocked with blocks above them like regular chests do 2024-02-12 14:57:24 -05:00
JazzMcNade 0d12bf1190 Relic Coins Sacks can be bartered with Piglins for loot.
Can always remove this if undesired or make it a tag, change the item, etc.
2024-02-10 21:23:05 -06:00
Lily 4f773f63cb fix hopper dupe bug 2024-02-09 22:04:01 -05:00
Lily 3f3ae74535 update .gitignore 2024-02-09 19:58:44 -05:00
Lily 7429fea802 fixed crash on dedicated servers (again) + made cases compatible with hoppers 2024-02-09 19:46:23 -05:00
Lily ac5cd47715 multiple display case bugfixes 2024-02-09 17:59:57 -05:00
JazzMcNade c635a1d331 Removed duped block tag entry 2024-02-09 01:08:35 -06:00
JazzMcNade cf2173a281 Opening a gilded chest now aggros piglins 2024-02-09 00:15:55 -06:00
JazzMcNade 710d37f453 Added piglin related tags for glided chests and relic coin related items 2024-02-08 23:52:36 -06:00
Lily 1fb3416211 implemented the display case 2024-02-08 22:46:16 -05:00
Lily f80a2850bb template type gem block 2024-02-05 19:42:36 -05:00
Lily 64fe6cefde Merge branch 'crystal_block' into tm-machine 2024-02-05 19:24:12 -05:00
Lily 26f337c0fd unlock tms when the player obtains them 2024-02-05 16:48:44 -05:00
JazzMcNade ac1c7d8b78 Mobs can now pathfind around Gilded Chests 2024-02-04 20:00:20 -06:00
Lily 92dd85643e Merge branch 'amo/sendout-upgrade' into gimmighoul_gholdengo
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/api/events/CobblemonEvents.kt
2024-02-03 19:45:47 -05:00
Lily 8849de1bfd Merge remote-tracking branch 'origin/amo/sendout-upgrade' into amo/sendout-upgrade
# Conflicts:
#	common/src/main/resources/assets/cobblemon/bedrock/particles/balls/capture/capturestar.particle.json
#	common/src/main/resources/assets/cobblemon/bedrock/particles/balls/capture/hisui/hisuicapturestar.particle.json
2024-02-03 19:44:41 -05:00
Lily 2a349697b4 colored gilded chest variants :3 2024-02-01 20:28:12 -05:00
jeffw773 9fc81c1b33 - Amethyst Shards now only feed into the correct slot 2024-02-01 09:04:02 -08:00
JazzMcNade 5c599dbb9a TM machine monitor switches on and off with player interaction. 2024-02-01 01:08:34 -06:00
jeffw773 d5a94ff784 - Changes to Blockstate when being used by player
- Plays sound when crafted with automation (might not be wanted later idk)
2024-01-31 16:16:52 -08:00
jeffw773 024f299486 - Comparator output from TMM now updates with respect to actions done by TM Machine 2024-01-31 12:06:23 -08:00
jeffw773 9a2b588568 - TM Machine can now Hopper out created TMs and spit out TMs and craft with Redstone signals if wanted 2024-01-31 10:53:26 -08:00
JazzMcNade b6fb5e8776 manual crafting 2024-01-31 03:00:40 -06:00
jeffw773 987b9bb275 - Sync fixes for TMM Inventory and UI slots 2024-01-30 14:35:33 -08:00
Lily 03bda4e27d fixes to be compatible with the updated held item event 2024-01-30 17:33:51 -05:00
apion b04ab8b95b Took some stuff out of CobblemonClient and put it in TMIten (clean up) 2024-01-30 16:52:22 -05:00
Lily 5d8abbd4c7 Merge branch 'main' into gimmighoul_gholdengo
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/Pokemon.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/pokemon/evolution/adapters/CobbledRequirementAdapter.kt
2024-01-30 16:51:56 -05:00
Lily 6b167a7a17 gilded chest variants are implemented, waiting for textures 2024-01-30 16:46:22 -05:00
Lily dab0623d61 Merge remote-tracking branch 'origin/tm-machine' into tm-machine 2024-01-30 14:16:21 -05:00
Lily cb5b6ec68b advancement obtain method 2024-01-30 14:16:11 -05:00
JazzMcNade ea57c9dc5d Fix for removeStack 2024-01-30 01:56:35 -06:00
apion d36c24075c Fixed rotations? 2024-01-29 16:48:21 -05:00
Lily d1a8122296 fixed rotation for gilded chest as well 2024-01-29 12:44:41 -05:00
Lily a8e6e684ab updated relic_coin_pouch.png 2024-01-29 12:22:55 -05:00
Lily 188fc2b5d9 fixed inconsistencies between the actual value of the facing property and the direction the coin pouch was facing 2024-01-29 12:19:50 -05:00
jeffw773 9abab091d9 - Materials feed into correct slots on TMM from hoppers 2024-01-28 13:01:49 -08:00
JazzMcNade e2e50d2f1b tm block now has a persistent inventory 2024-01-27 20:37:13 -06:00
jeffw773 4ac88ba192 - fixes to TM instances and isReadyToCraft conditional 2024-01-27 13:00:34 -08:00
jeffw773 37eccce8b1 - tweaks to tms 2024-01-27 12:14:13 -08:00
jeffw773 e44db7c380 - Filter Disc renders properly with client 2024-01-27 11:05:21 -08:00
JazzMcNade d57bb42720 Fix for shift click not always applying the filter 2024-01-27 05:13:19 -06:00
jeffw773 f0c44f040f Merge remote-tracking branch 'origin/tm-machine' into tm-machine 2024-01-26 21:52:45 -08:00
jeffw773 2d6e2f87ed - Hoppers and comparators are working for making TMs and Blank TMs 2024-01-26 21:52:27 -08:00
apion 8b4193a5db Fixed rotations 2024-01-26 22:54:33 -05:00
apion 667dfe32e3 More fun stuff 2024-01-26 22:27:13 -05:00
apion 218253765e Merge branch 'main' into tm-machine 2024-01-26 18:50:37 -05:00
Lily 7a15e6b004 Merge remote-tracking branch 'origin/amo/sendout-upgrade' into amo/sendout-upgrade
# Conflicts:
#	CHANGELOG.md
#	common/src/main/resources/assets/cobblemon/bedrock/particles/balls/capture/hisui/hisuicapturestar.particle.json
2024-01-26 17:46:34 -05:00
Lily 6ee6beddeb capture particles... again 2024-01-26 17:45:31 -05:00
Lily b92960da77 Merge branch 'main' into amo/sendout-upgrade 2024-01-26 17:29:13 -05:00
apion ff082adf67 Start working on rendering different color disks in the render machine 2024-01-26 17:27:49 -05:00
apion 66959816ef Merge branch 'main' into tm-machine
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/player/PlayerData.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
#	common/src/main/resources/cobblemon-common.accesswidener
2024-01-26 15:52:23 -05:00
jeffw773 0e85835a87 Merge remote-tracking branch 'origin/tm-machine' into tm-machine 2024-01-26 12:44:35 -08:00
jeffw773 e165d4958d - TM Machine Inventory Disc 2024-01-26 12:44:18 -08:00
JazzMcNade e0736e1988 TM Block is no longer treated as 2 blocks tall for placement. 2024-01-26 02:04:24 -06:00
jeffw773 724ff86a21 - TM Machine Inventory Test 2024-01-25 23:03:38 -08:00
jeffw773 97427fa58b - TM Machine Entity functioning 2024-01-25 17:04:38 -08:00
jeffw773 c9d47a3e21 - More TM Mechanics 2024-01-24 22:30:56 -08:00
jeffw773 c5c884ca2e - Testing New TM Functionality 2024-01-24 15:58:42 -08:00
jeffw773 f086c32b95 - TM Machine Testing 2024-01-24 14:25:49 -08:00
jeffw773 ecea176300 - Added placeholder Type Gem sprites for TM Materials 2024-01-23 16:11:39 -08:00
jeffw773 c00413bb32 - Added Back buttons to TM Machine 2024-01-23 14:28:57 -08:00
jeffw773 be78012ce6 - TM Machine can now scroll through TMs
- Fixed how Scroll List and TM entries are handled so they can be interacted with properly
2024-01-23 11:01:05 -08:00
jeffw773 3b285cc8bd - Changes to Relic Coin sack default placement direction for Structures 2024-01-19 10:51:21 -08:00
jeffw773 4dead98e6b - Pesky little chest Gimmieghouls spawn facing the direction of the chest block now 2024-01-18 18:16:09 -08:00
jeffw773 db36f8da2a - Gimmieghoul Chest and Gilded Chest now face the player when placed down 2024-01-18 16:07:45 -08:00
Paul 7215860d74 Merge remote-tracking branch 'origin/gimmighoul_gholdengo' into gimmighoul_gholdengo 2024-01-14 08:08:04 +10:00
Paul dc899ffc1b Fixed gimmighoul stuff for realsies this time 2024-01-14 08:07:49 +10:00
Lily 88fe55bbb9 pouch blocks and bugfixes 2024-01-13 16:23:39 -05:00
Paul 3d3c421b5c Fixed syncing issues with species feature providers 2024-01-12 18:15:10 +10:00
jeffw773 3844dd6314 - Added mouseClicked, mouseScrolled, mouseDragged to TMScrollingList (but still no work) 2024-01-11 12:34:14 -08:00
Lily 080077189f tm machine ui 2024-01-10 19:45:04 -05:00
Lily 89a4edb327 drops and creative inv 2024-01-09 15:59:33 -05:00
Lily ab37580258 gimmighoul chests crash ur game a little bit less now :o 2024-01-09 14:56:50 -05:00
Lily c116c01fd6 licenses 2024-01-09 14:16:09 -05:00
Lily e3ad8f8079 gimmi now challenges the player on reveal 2024-01-09 13:54:35 -05:00
Lily a491d508b6 gilded chest visuals, loot tables/recipes, sounds, etc. 2024-01-09 12:38:58 -05:00
apion 02fb825328 Update Gimmighoul model, add disguised chest block entity, gilded chests now save contents 2024-01-08 14:05:09 -05:00
Paul 8df6eea107 Fixed various issues with the gilded chest and its animations 2024-01-08 11:00:20 +10:00
apion 1376bb68f4 Tried again, failed again 2024-01-07 11:53:38 -05:00
apion beb8f2ccf1 Merge branch 'main' into gimmighoul_gholdengo
# Conflicts:
#	CHANGELOG.md
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonNetwork.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/CobblemonSounds.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/api/storage/party/PlayerPartyStore.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/CobblemonClient.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/PoseableEntityModel.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/render/models/blockbench/repository/PokemonModelRepository.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/net/messages/PokemonDTO.kt
#	common/src/main/resources/assets/cobblemon/lang/en_us.json
#	common/src/main/resources/assets/cobblemon/sounds.json
#	common/src/main/resources/data/cobblemon/species/generation9/gimmighoul.json
2024-01-07 10:41:34 -05:00
jeffw773 4fa86c7742 - Added Eiko's new TM Machine Textures and fixed inventory slot locations 2024-01-04 13:11:00 -08:00
Lily b0d57dd875 coin bags and blocks and stuff 2024-01-04 15:56:58 -05:00
Lily 6c09e2b553 Merge remote-tracking branch 'origin/gimmighoul_gholdengo' into gimmighoul_gholdengo 2024-01-04 13:47:21 -05:00
Lily 593104fb0a BIG COIN BAG!! 2024-01-04 13:45:43 -05:00
apion e314664731 Throw some stuff in and hope Hiro can fix it (I need a hero! I'm holding out for a hero...) 2024-01-04 07:31:29 -05:00
apion d0a38a9704 Add chest assets, do some work on getting chest animation working 2024-01-04 06:48:59 -05:00
jeffw773 3c9cff5027 - Tweaked position of TM Machine GUI slightly as well as tooltips and inventory slots 2024-01-03 23:02:07 -08:00
Lily 6e155d7651 Merge remote-tracking branch 'origin/gimmighoul_gholdengo' into gimmighoul_gholdengo 2024-01-04 00:04:41 -05:00
Lily ae8f8a2fb8 working run button!!! wow!!! 2024-01-04 00:04:26 -05:00
apion 5a6a83aac1 Cleanup, add new model 2024-01-03 23:50:55 -05:00
Lily a1047e05d4 psychic trim update + updated type icons 2024-01-03 20:19:38 -05:00
Lily b7c1157c5c slight changes to the TM UI 2024-01-03 13:47:41 -05:00
apion cc8a51abcd TM Menu actually rendering now 2024-01-03 06:45:25 -05:00
apion 38174155bf Improved registry process for HandledScreens 2024-01-03 03:05:58 -05:00
Lily 2698006f93 filterTms function for when the UI is implemented 2024-01-03 00:30:50 -05:00
Lily aa38674fc8 TMM sounds and subtitles 2024-01-02 22:40:04 -05:00
apion 9ca0742386 Added fixed TMM model, added system to register HandledScreens 2024-01-02 21:53:50 -05:00
Lily 32d291890e TM colors and type gem items 2024-01-02 18:36:04 -05:00
Lily 188ae2ac85 changes to the gimmighoul evolution system. added relic coins, updated bars, removed NetheriteMaterials and GoldMaterials 2024-01-02 14:09:52 -05:00
Lily 381fd1d5e2 tm machine visuals 2024-01-02 12:42:32 -05:00
Lily a2fbe16ede textures partially 2024-01-02 12:20:32 -05:00
Lily 0b478ebd5a working coloring for tm items based on two ints given in the tm json file 2024-01-02 11:40:27 -05:00
Lily b0b07e8edd functional obtain methods for tms, with an option to passively check them 2024-01-01 15:40:07 -05:00
Lily 95a0f9603c gilded chest model stuffs 2023-12-27 14:54:28 -05:00
Lily 824773150a more tm item stuff 2023-12-15 16:54:28 -05:00
Lily 3dc285cc70 tm item?!?!?! what the flip?!? 2023-12-11 19:48:36 -05:00
Lily d6fbb70db0 add level ranges to DisguisedPokemonBlock 2023-12-11 16:10:13 -05:00
Lily 150a5c668a redid disguised gimmighouls 2023-12-11 14:55:43 -05:00
apion e12d0bea66 Added TMBlock (lol) 2023-12-01 01:19:28 -05:00
Lily 1bf01a2826 more bar stuff 2023-11-30 15:16:32 -05:00
Lily 4d1bf677a1 bar color update 2023-11-29 21:49:06 -05:00
Lily 069f56acfd gholdengo/gimmighoul are basically done, gilded chests still unfinished 2023-11-29 20:32:20 -05:00
Lily 19cdc8fbef Merge remote-tracking branch 'origin/gimmighoul_gholdengo' into gimmighoul_gholdengo 2023-11-27 17:31:27 -05:00
Lily 1408b94f06 started on gilded chests 2023-11-27 17:31:17 -05:00
Paul de081dc514 Added aspects support for int range features 2023-11-27 11:53:06 +10:00
apion 5bc4994091 Added learned tms to PlayerData, added a way to obtain TMs 2023-11-22 05:01:09 -05:00
apion 2b992dfd51 Setup TM registry 2023-11-22 03:22:36 -05:00
Lily 1538f12934 more gimmighoul changes 2023-11-14 20:59:10 -05:00
Paul 857f67604c Fixed some logic in held item event + gimmighoul item 2023-11-14 19:30:27 +10:00
Paul ae49e3f977 Fixed gold materials data registry not being loaded 2023-11-14 19:18:45 +10:00
Lily ec01a03820 pushed some stuff idk what to put here 2023-11-14 00:47:15 -05:00
Lily 96cc3671da Merge branch 'held_item_event' into gimmighoul_gholdengo 2023-11-13 18:36:09 -05:00
Lily ad7c9c560c add an event for updating a pokemon's held item 2023-11-13 18:33:45 -05:00
Tyzillion ee15ff6b35 Corrected Gimmighoul chest and roaming size. 2023-11-13 16:49:00 -05:00
Tyzillion ca83103703 Bling, bling, every time I come around your city. 2023-11-13 16:33:07 -05:00
Tyzillion 1aeb894b09 Merge branch 'main' of https://gitlab.com/cable-mc/cobblemon into gimmighoul_gholdengo 2023-11-13 13:42:38 -05:00
Lily 8e9ab26963 json files for gold and netherite materials (doesnt work in-game yet) 2023-11-13 18:09:34 +00:00
Paul 44b4fbcfe3 Minor license thing 2023-11-13 20:04:29 +10:00
Paul 42cc1dd8a9 Upgraded species features so they can be synced and rendered in summary screen 2023-11-13 19:39:17 +10:00
Lily d69310e52c re-do the way the other stats screen draws stuff 2023-11-10 20:15:40 -05:00
Lily a0f89274d1 started work on SummaryValue properties (CW: messy, incomplete systems) 2023-11-10 18:57:11 -05:00
1507 changed files with 54123 additions and 1649 deletions
+9 -1
View File
@@ -33,4 +33,12 @@ common/src/main/resources/assets/cobblemon/showdown
# Generated files
*/generated/resources/*
/fabric/src/generated/resources/cobblemon-common.accesswidener
/.ignored/
/.ignored/
# Logs
common/logs
fabric/logs
forge/logs
# MacOS
.DS_Store
+28 -1
View File
@@ -14,12 +14,23 @@
- Added the hidden "True Vivillionaire" advancement.
- The "Poké Ball" Vivillon variant is now obtainable by evolving a Spewpa after obtaining the "Aspiring Vivillionaire" advancement.
- Added dynamic lighting support for any Species or Forms when using [LambDynamicLights](https://modrinth.com/mod/lambdynamiclights) or [Dynamic Lights Reforged](https://www.curseforge.com/minecraft/mc-mods/dynamiclights-reforged), at this time we don't ship with any defaults for this feature but addon creators can make use of this feature.
- Added a Display Case block! Use it to display your archaeological findings, or other items.
- Added 6 Pokémon-themed Pottery Sherds, obtained from ruin structures.
- Added a Pokémon-themed armor trim, obtained from ruin structures.
- You can now find Nether Fire Stone Ore in, surprisingly, the Nether.
- Added effects for many Poké Balls when sending out Pokémon or capturing.
- Added a new send-out ball toss animation that showcases the ball used for the Pokémon.
- Added simple move animations for Pokémon in battle with a proof of concept for more complicated animations.
- Added the EV Boosting Feathers.
- Added Type Gems.
- Added various new tags for searchability and better compatibility.
- Added the [Ability Capsule](https://bulbapedia.bulbagarden.net/wiki/Ability_Capsule) and [Patch](https://bulbapedia.bulbagarden.net/wiki/Ability_Patch), these have no acquisition method for now and only work as intended with the traditional ability format of 1 to 2 common abilities and 1 hidden ability.
- Added Gilded Chests, available in all Apricorn colors.
- Added Relic Coins, Relic Coin Pouches, and Relic Coin Sacks.
- Added Gimmighoul.
- Using Relic Coins, you can increase Gimmighoul's Coin Stash. Upon reaching 999, it can be evolved into Gholdengo.
- Additionally, if you give Gimmighoul a Netherite Scrap, you can increase its Netherite Stash. Who knows what might happen if it fills up?
- Completed the advancement for placing a Tumblestone near Lava or Magma to make it grow.
### Changes
- Pokémon now transition more smoothly between different poses.
@@ -32,6 +43,7 @@
- Updated movesets to add Gen 9 DLC moves.
- Apricorn Sprouts can now be planted directly on Apricorn leaves to create a new Apricorn of the same color.
- Added tooltips to interaction GUIs to make it clearer what they do.
- Poké Ball recipes now use tags for their center ingredient, rather than specificied Copper Ingot, Iron Ingot, Gold Ingot, and Diamond, allowing for further datapack customisation.
### Fixes
- Re-added the recipe for the Iron Vitamin.
@@ -46,6 +58,14 @@
- Fixed Poké Balls freezing in an open state if you look away when it's meant to close.
- Adds a translation string name to the Empty Pokeball entity.
- Fixed very many issues with Bedrock particle effects.
- Fixed Arbok patterns not being as specific in spawning as they were intended.
- Fixed Pokémon not avoiding danger at all when pathfinding.
- Fixed Sobble line not being able to swim or breathe underwater.
- Fixed incompatibility with [Just Enough Resources](https://modrinth.com/mod/just-enough-resources-jer).
- Fixed incompatibility with [Visual Overhaul](https://modrinth.com/mod/visual-overhaul).
- Fixed an issue that allowed Pokemon to spawn on rails.
- Fixed the data set on Pokémon when abilities are given via properties such as in ``/spawnpokemon``.
- Fixed sometimes Pokémon losing hidden abilities when evolving, this fix will only take effect to newly created Pokémon.
### Developer
- Significantly changed the way properties in PokemonEntity work. This could break some plugins (not in a big way, but changes will be needed).
@@ -57,6 +77,12 @@
- Made PokemonProperties.parse more interoperable with Java.
- Added the ``HeldItemEvent``, this comes with 2 implementation ``HeldItemEvent.Pre`` and ``HeldItemEvent.Post``.
- API to support dynamic lighting in any mod that implements this feature has been added to all Species & Forms, see LightingData class for details.
- Added a Mixin for the BrewingStandScreenHandler to fix the incorrect handling of quickMove in Fabric.
- Corrections to the ability implementation have been made that make ``Ability.forced`` function as intended and never reroll an ability for a Pokémon, please check your implementations to ensure the corrected behavior is intended for your use case.
- ``Pokemon.ability`` no longer has a public setter, please migrate to using ``Pokemon#updateAbility``, this handles the ability coordinate storage for you when necessary.
- ``Ability.forced``, ``Ability.index`` and ``Ability.priority`` have all had their setters internalized, there is no longer any need to manually adjust these migrate to the method mentioned above to handle that process for you.
- ``AbilityChanger`` has been added to API alongside some implementations, this is subject to change as the ability capsule and patch currently only expect the traditional behaviour in the Pokémon games of 1 to 2 regular abilities and 1 hidden.
- ``Pokemon#rollAbility`` has been added, this rerolls a legal ability for the Pokémon.
### Datapack & Resourcepack Creators
- The maximum amount of fossils that can fit in the Fossil Analyzer can be adjusted in the config.
@@ -91,6 +117,7 @@
}
```
- Following up on this change ``light_source`` was removed as a possible shoulder effect due to becoming unnecessary and never having had a default implementation.
- Added the item tag ``cobblemon:ability_changers``, contains the ``cobblemon:ability_capsule`` & ``cobblemon:ability_patch`` by default.
## [1.4.1 (December 23rd, 2023)](#1-4-1)
@@ -1026,4 +1053,4 @@ Thank you so much to all of our community translators that bring the mod to the
- Added species, ability, and move translations for `ko_ko`, `jp_jp`, `fr_fr`, `es_es`, `it_it`, and `zh_cn`.
## [1.0.0 (November 12th, 2022)](#1-0-0)
- Initial release.
- Initial release.
+2 -2
View File
@@ -9,5 +9,5 @@ version = "${project.property("mod_version")}+${project.property("mc_version")}"
val isSnapshot = project.property("snapshot")?.equals("true") ?: false
if (isSnapshot) {
version = "$version-SNAPSHOT-${versioning.info.branch}-${versioning.info.build}"
}
version = "$version-${versioning.info.branch}-${versioning.info.build}-SNAPSHOT"
}
+1
View File
@@ -0,0 +1 @@
[19:57:25] [Test worker/INFO]: Booting LaxPermissionValidator, permissions will be checked using Minecrafts permission level system, see https://minecraft.fandom.com/wiki/Permission_level
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonItems;
import net.minecraft.entity.mob.PiglinBrain;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
@Mixin(PiglinBrain.class)
public class PiglinBarterMixin {
@Inject(method = "acceptsForBarter", at = @At(value = "RETURN"), cancellable = true)
private static void cobblemon$acceptsForBarter(ItemStack stack, CallbackInfoReturnable<Boolean> ci) {
if(!ci.getReturnValue()) {
//TODO: Make a tag?
if(stack.isOf(CobblemonItems.RELIC_COIN_POUCH))
ci.setReturnValue(true);
}
}
}
@@ -0,0 +1,31 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.mixin;
import com.cobblemon.mod.common.CobblemonItems;
import net.minecraft.entity.EquipmentSlot;
import net.minecraft.entity.mob.PiglinEntity;
import net.minecraft.item.ItemStack;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(PiglinEntity.class)
public abstract class PiglinEntityMixin {
@Inject(method = "equipToOffHand", at = @At(value = "HEAD"), cancellable = true)
public void cobblemon$isValidBarteringItem(ItemStack stack, CallbackInfo ci) {
PiglinEntity entity = (PiglinEntity)(Object) this;
if (stack.isOf(CobblemonItems.RELIC_COIN_POUCH)) {
entity.equipStack(EquipmentSlot.OFFHAND, stack);
entity.updateDropChances(EquipmentSlot.OFFHAND);
ci.cancel();
}
}
}
@@ -0,0 +1,20 @@
package com.cobblemon.mod.common.mixin.invoker;
import net.minecraft.block.AbstractBlock;
import net.minecraft.block.BlockSetType;
import net.minecraft.block.PressurePlateBlock;
import net.minecraft.resource.featuretoggle.FeatureSet;
import net.minecraft.screen.ScreenHandler;
import net.minecraft.screen.ScreenHandlerType;
import net.minecraft.screen.ScreenHandlerType.Factory;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.gen.Invoker;
@Mixin(ScreenHandlerType.class)
public interface ScreenHandlerTypeInvoker {
@Invoker("<init>")
static <T extends ScreenHandler> ScreenHandlerType<T> cobblemon$create(Factory<T> factory, FeatureSet requiredFeatures) {
throw new UnsupportedOperationException();
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.api.drop.ItemDropEntry
import com.cobblemon.mod.common.api.events.CobblemonEvents.BATTLE_VICTORY
import com.cobblemon.mod.common.api.events.CobblemonEvents.DATA_SYNCHRONIZED
import com.cobblemon.mod.common.api.events.CobblemonEvents.EVOLUTION_COMPLETE
import com.cobblemon.mod.common.api.events.CobblemonEvents.HELD_ITEM_UPDATED
import com.cobblemon.mod.common.api.events.CobblemonEvents.LEVEL_UP_EVENT
import com.cobblemon.mod.common.api.events.CobblemonEvents.POKEMON_CAPTURED
import com.cobblemon.mod.common.api.events.CobblemonEvents.TRADE_COMPLETED
@@ -34,9 +35,7 @@ import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffectRegistry
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceCalculator
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
import com.cobblemon.mod.common.api.pokemon.experience.StandardExperienceCalculator
import com.cobblemon.mod.common.api.pokemon.feature.ChoiceSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.FlagSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.*
import com.cobblemon.mod.common.api.pokemon.helditem.HeldItemProvider
import com.cobblemon.mod.common.api.pokemon.stats.EvCalculator
import com.cobblemon.mod.common.api.pokemon.stats.Generation8EvCalculator
@@ -84,6 +83,7 @@ import com.cobblemon.mod.common.config.starter.StarterConfig
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.events.AdvancementHandler
import com.cobblemon.mod.common.events.ServerTickHandler
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.net.messages.client.settings.ServerSettingsPacket
import com.cobblemon.mod.common.permission.LaxPermissionValidator
@@ -94,6 +94,7 @@ import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.feature.TagSeasonResolver
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
import com.cobblemon.mod.common.pokemon.misc.GimmighoulStashHandler
import com.cobblemon.mod.common.pokemon.properties.HiddenAbilityPropertyType
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.pokemon.properties.tags.PokemonFlagProperty
@@ -130,6 +131,7 @@ import net.minecraft.entity.data.TrackedDataHandlerRegistry
import net.minecraft.item.Items
import net.minecraft.item.NameTagItem
import net.minecraft.registry.RegistryKey
import net.minecraft.sound.SoundCategory
import net.minecraft.util.WorldSavePath
import net.minecraft.world.World
import org.apache.logging.log4j.LogManager
@@ -230,6 +232,9 @@ object Cobblemon {
}
}
// Register the grow_tumblestone advancement
PlatformEvents.RIGHT_CLICK_BLOCK.subscribe { AdvancementHandler.onTumbleStonePlaced(it) }
PlatformEvents.CHANGE_DIMENSION.subscribe {
it.player.party().forEach { pokemon -> pokemon.entity?.recallWithAnimation() }
}
@@ -253,6 +258,7 @@ object Cobblemon {
SpeciesFeatures.types["choice"] = ChoiceSpeciesFeatureProvider::class.java
SpeciesFeatures.types["flag"] = FlagSpeciesFeatureProvider::class.java
SpeciesFeatures.types["integer"] = IntSpeciesFeatureProvider::class.java
SpeciesFeatures.register(
DataKeys.CAN_BE_MILKED,
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.block.entity.FossilMultiblockEntity
import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import com.cobblemon.mod.common.block.entity.*
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.registry.Registries
@@ -44,6 +45,20 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
val SIGN: BlockEntityType<CobblemonSignBlockEntity> = this.create("sign", BlockEntityType.Builder.create(::CobblemonSignBlockEntity, CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN).build(null))
@JvmField
val HANGING_SIGN: BlockEntityType<CobblemonHangingSignBlockEntity> = this.create("hanging_sign", BlockEntityType.Builder.create(::CobblemonHangingSignBlockEntity, CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN).build(null))
@JvmField
val TM_BLOCK: BlockEntityType<TMBlockEntity> = this.create("tm_block", BlockEntityType.Builder.create(::TMBlockEntity, CobblemonBlocks.TM_MACHINE).build(null))
@JvmField
val GILDED_CHEST: BlockEntityType<GildedChestBlockEntity> = this.create("chest", BlockEntityType.Builder.create(::GildedChestBlockEntity,
CobblemonBlocks.GILDED_CHEST,
CobblemonBlocks.BLUE_GILDED_CHEST,
CobblemonBlocks.YELLOW_GILDED_CHEST,
CobblemonBlocks.PINK_GILDED_CHEST,
CobblemonBlocks.BLACK_GILDED_CHEST,
CobblemonBlocks.WHITE_GILDED_CHEST,
CobblemonBlocks.GREEN_GILDED_CHEST,
CobblemonBlocks.GIMMIGHOUL_CHEST
).build(null))
@JvmField
val FOSSIL_MULTIBLOCK: BlockEntityType<FossilMultiblockEntity> = this.create("fossil_multiblock",
@@ -65,4 +80,9 @@ object CobblemonBlockEntities : PlatformRegistry<Registry<BlockEntityType<*>>, R
CobblemonBlocks.FOSSIL_ANALYZER
).build(null)
)
@JvmField
val DISPLAY_CASE: BlockEntityType<DisplayCaseBlockEntity> = this.create("display_case",
BlockEntityType.Builder.create(::DisplayCaseBlockEntity, CobblemonBlocks.DISPLAY_CASE).build(null)
)
}
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.mixin.invoker.*
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.block.BerryBlock
import com.cobblemon.mod.common.block.chest.GildedChestBlock
import com.cobblemon.mod.common.block.sign.CobblemonHangingSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonSignBlock
import com.cobblemon.mod.common.block.sign.CobblemonWallHangingSignBlock
@@ -151,11 +152,11 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
val YELLOW_APRICORN_SAPLING = this.create("yellow_apricorn_sapling", ApricornSaplingBlock(PLANT_PROPERTIES, Apricorn.YELLOW))
@JvmField
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val MEDICINAL_LEEK = this.create("medicinal_leek", MedicinalLeekBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DULL_RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MEDICINAL_LEEK_SOUNDS)))
@JvmField
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
val ENERGY_ROOT = this.create("energy_root", EnergyRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DIRT_BROWN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.ENERGY_ROOT_SOUNDS)))
@JvmField
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.ROOTS)))
val BIG_ROOT = this.create("big_root", BigRootBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).burnable().mapColor(MapColor.DARK_GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.BIG_ROOT_SOUNDS)))
@JvmField
val REVIVAL_HERB = this.create("revival_herb", RevivalHerbBlock(AbstractBlock.Settings.create().pistonBehavior(PistonBehavior.DESTROY).mapColor(MapColor.DARK_GREEN).burnable().noCollision().breakInstantly().sounds(BlockSoundGroup.CROP)))
@@ -191,6 +192,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.mapColor(MapColor.TERRACOTTA_ORANGE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
@@ -198,6 +200,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.mapColor(MapColor.LIGHT_BLUE)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@JvmField
@@ -205,6 +208,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.mapColor(MapColor.TERRACOTTA_BLACK)
.strength(1.0F)
.sounds(CobblemonSounds.TUMBLESTONE_BLOCK_SOUNDS)
.requiresTool()
.instrument(Instrument.BASEDRUM)
))
@@ -223,6 +227,99 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val YELLOW_APRICORN = apricornBlock("yellow_apricorn", Apricorn.YELLOW)
@JvmField
val RELIC_COIN_POUCH = create(
"relic_coin_pouch",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f)
.nonOpaque(), true
)
)
@JvmField
val RELIC_COIN_SACK = create(
"relic_coin_sack",
CoinPouchBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.COIN_POUCH_SOUNDS)
.pistonBehavior(PistonBehavior.DESTROY)
.strength(0.4f), false
))
@JvmField
val GILDED_CHEST = create(
"gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.RED
)
)
@JvmField
val BLUE_GILDED_CHEST = create(
"blue_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.BLUE
)
)
@JvmField
val BLACK_GILDED_CHEST = create(
"black_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.BLACK
)
)
@JvmField
val YELLOW_GILDED_CHEST = create(
"yellow_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.YELLOW
)
)
@JvmField
val WHITE_GILDED_CHEST = create(
"white_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.WHITE
)
)
@JvmField
val GREEN_GILDED_CHEST = create(
"green_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.GREEN
)
)
@JvmField
val PINK_GILDED_CHEST = create(
"pink_gilded_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.PINK
)
)
@JvmField
val GIMMIGHOUL_CHEST = create(
"gimmighoul_chest",
GildedChestBlock(
AbstractBlock.Settings.copy(Blocks.CHEST).nonOpaque().sounds(CobblemonSounds.GILDED_CHEST_SOUNDS),
GildedChestBlock.Type.FAKE
)
)
@JvmField
val MONITOR = create(
"monitor",
@@ -255,7 +352,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
RestorationTankBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.IRON_GRAY)
.sounds(BlockSoundGroup.METAL)
.sounds(BlockSoundGroup.GLASS)
.pistonBehavior(PistonBehavior.BLOCK)
.requiresTool()
.strength(5.0F, 6.0F)
@@ -288,19 +385,55 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
.luminance { if ((it.get(PCBlock.ON) as Boolean) && (it.get(PCBlock.PART) == PCBlock.PCPart.TOP)) 10 else 0 }
)
)
val TM_MACHINE = create(
"tm_machine",
TMBlock(
AbstractBlock.Settings.create()
.mapColor(MapColor.GREEN)
.sounds(BlockSoundGroup.METAL)
.pistonBehavior(PistonBehavior.BLOCK)
.nonOpaque()
.luminance { if (it.get(TMBlock.ON) as Boolean) 10 else 0 }
)
)
@JvmField
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val DISPLAY_CASE = create(
"display_case",
DisplayCaseBlock(
AbstractBlock.Settings.create()
.sounds(CobblemonSounds.DISPLAY_CASE_SOUNDS)
.nonOpaque()
.pistonBehavior(PistonBehavior.BLOCK)
.mapColor(MapColor.STONE_GRAY)
.strength(0.3f)
)
)
@JvmField
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val MODIFICATION_STATION = create(
"modification_station",
ModStationBlock(
AbstractBlock.Settings.create()
.sounds(BlockSoundGroup.WOOD)
.nonOpaque()
.pistonBehavior(PistonBehavior.BLOCK)
.mapColor(MapColor.BROWN)
.strength(0.3f)
)
)
val RED_MINT = create("red_mint", MintBlock(MintType.RED, AbstractBlock.Settings.create().mapColor(MapColor.RED).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
@JvmField
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val BLUE_MINT = create("blue_mint", MintBlock(MintType.BLUE, AbstractBlock.Settings.create().mapColor(MapColor.BLUE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
@JvmField
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val CYAN_MINT = create("cyan_mint", MintBlock(MintType.CYAN, AbstractBlock.Settings.create().mapColor(MapColor.CYAN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
@JvmField
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val PINK_MINT = create("pink_mint", MintBlock(MintType.PINK, AbstractBlock.Settings.create().mapColor(MapColor.PINK).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
@JvmField
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(BlockSoundGroup.CROP)))
val GREEN_MINT = create("green_mint", MintBlock(MintType.GREEN, AbstractBlock.Settings.create().mapColor(MapColor.GREEN).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
@JvmField
val WHITE_MINT = create("white_mint", MintBlock(MintType.WHITE, AbstractBlock.Settings.create().mapColor(MapColor.WHITE).noCollision().ticksRandomly().breakInstantly().sounds(CobblemonSounds.MINT_SOUNDS)))
@JvmField
val PASTURE = create(
@@ -323,6 +456,67 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
@JvmField
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
@JvmField
val NORMAL_TYPE_GEM_BLOCK = this.create("normal_type_gem_block", Block(AbstractBlock.Settings.copy(Blocks.AMETHYST_BLOCK)))
@JvmField
val NORMAL_TYPE_GEM = typeGem(
"normal_type_gem",
GrowableStoneBlock.STAGE_3,
7,
3,
null,
NORMAL_TYPE_GEM_BLOCK
)
@JvmField
val LARGE_BUDDING_NORMAL_TYPE_GEM = typeGem(
"large_budding_normal_type_gem",
GrowableStoneBlock.STAGE_2,
5,
3,
NORMAL_TYPE_GEM,
NORMAL_TYPE_GEM_BLOCK
)
@JvmField
val MEDIUM_BUDDING_NORMAL_TYPE_GEM = typeGem(
"medium_budding_normal_type_gem",
GrowableStoneBlock.STAGE_1,
4,
3,
LARGE_BUDDING_NORMAL_TYPE_GEM,
NORMAL_TYPE_GEM_BLOCK
)
@JvmField
val SMALL_BUDDING_NORMAL_TYPE_GEM = typeGem(
"small_budding_normal_type_gem",
GrowableStoneBlock.STAGE_0,
3,
4,
MEDIUM_BUDDING_NORMAL_TYPE_GEM,
NORMAL_TYPE_GEM_BLOCK
)
private fun typeGem(
name: String,
stage: Int,
height: Int,
xzOffset: Int,
nextStage: Block?,
baseBlock: Block
): TypeGemBlock {
return this.create(name, TypeGemBlock(
settings = AbstractBlock.Settings.create()
.pistonBehavior(PistonBehavior.DESTROY)
.nonOpaque()
.strength(1.5F)
.sounds(CobblemonSounds.TYPE_GEM_SOUNDS),
stage = stage,
height = height,
xzOffset = xzOffset,
nextStage = nextStage,
baseBlock = baseBlock
))
}
/**
* Returns a map of all the blocks that can be stripped with an axe in the format of input - output.
*
@@ -333,8 +527,6 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
APRICORN_LOG to STRIPPED_APRICORN_LOG
)
private fun apricornBlock(name: String, apricorn: Apricorn): ApricornBlock = this.create(name, ApricornBlock(AbstractBlock.Settings.create().mapColor(apricorn.mapColor()).ticksRandomly().strength(Blocks.OAK_LOG.hardness, Blocks.OAK_LOG.blastResistance).sounds(BlockSoundGroup.WOOD).nonOpaque(), apricorn))
private fun tumblestoneBlock(name: String, stage: Int, height: Int, xzOffset: Int, nextStage: Block?) : Block {
@@ -441,7 +633,7 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, RegistryKey<Registry<
private fun berryBlock(name: String): BerryBlock {
val identifier = cobblemonResource("${name}_berry")
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(BlockSoundGroup.CROP).strength(0.2F)))
val block = this.create(identifier.path, BerryBlock(identifier, AbstractBlock.Settings.copy(Blocks.WHEAT).dynamicBounds().sounds(CobblemonSounds.BERRY_BUSH_SOUNDS).strength(0.2F)))
this.berries[identifier] = block
return block
}
@@ -26,6 +26,7 @@ import net.minecraft.registry.tag.TagKey
import net.minecraft.resource.ResourceManager
import net.minecraft.resource.ResourceReloader
import net.minecraft.resource.ResourceType
import net.minecraft.screen.ScreenHandlerType
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.Text
@@ -158,6 +159,8 @@ interface CobblemonImplementation {
*/
fun registerResourceReloader(identifier: Identifier, reloader: ResourceReloader, type: ResourceType, dependencies: Collection<Identifier>)
fun registerScreenHandlerType(identifier: Identifier, screenHandlerType: ScreenHandlerType<*>)
/**
* TODO
*
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common
import com.cobblemon.mod.common.api.item.ability.AbilityChanger
import com.cobblemon.mod.common.api.mulch.MulchVariant
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
@@ -29,6 +30,7 @@ import com.cobblemon.mod.common.item.berry.PortionHealingBerryItem
import com.cobblemon.mod.common.item.berry.StatusCuringBerryItem
import com.cobblemon.mod.common.item.berry.VolatileCuringBerryItem
import com.cobblemon.mod.common.item.interactive.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityChangeItem
import com.cobblemon.mod.common.platform.PlatformRegistry
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.helditem.CobblemonHeldItemManager
@@ -229,6 +231,22 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val APRICORN_SIGN = this.create("apricorn_sign", SignItem(Item.Settings().maxCount(16), CobblemonBlocks.APRICORN_SIGN, CobblemonBlocks.APRICORN_WALL_SIGN))
@JvmField
val APRICORN_HANGING_SIGN = this.create("apricorn_hanging_sign", HangingSignItem(CobblemonBlocks.APRICORN_HANGING_SIGN, CobblemonBlocks.APRICORN_WALL_HANGING_SIGN, Item.Settings().maxCount(16)))
@JvmField
val GILDED_CHEST = this.create("gilded_chest", BlockItem(CobblemonBlocks.GILDED_CHEST, Item.Settings()))
@JvmField
val BLUE_GILDED_CHEST = this.create("blue_gilded_chest", BlockItem(CobblemonBlocks.BLUE_GILDED_CHEST, Item.Settings()))
@JvmField
val YELLOW_GILDED_CHEST = this.create("yellow_gilded_chest", BlockItem(CobblemonBlocks.YELLOW_GILDED_CHEST, Item.Settings()))
@JvmField
val PINK_GILDED_CHEST = this.create("pink_gilded_chest", BlockItem(CobblemonBlocks.PINK_GILDED_CHEST, Item.Settings()))
@JvmField
val BLACK_GILDED_CHEST = this.create("black_gilded_chest", BlockItem(CobblemonBlocks.BLACK_GILDED_CHEST, Item.Settings()))
@JvmField
val WHITE_GILDED_CHEST = this.create("white_gilded_chest", BlockItem(CobblemonBlocks.WHITE_GILDED_CHEST, Item.Settings()))
@JvmField
val GREEN_GILDED_CHEST = this.create("green_gilded_chest", BlockItem(CobblemonBlocks.GREEN_GILDED_CHEST, Item.Settings()))
@JvmField
val GIMMIGHOUL_CHEST = this.create("gimmighoul_chest", BlockItem(CobblemonBlocks.GIMMIGHOUL_CHEST, Item.Settings()))
@JvmField
val RESTORATION_TANK = blockItem("restoration_tank", CobblemonBlocks.RESTORATION_TANK)
@@ -240,8 +258,13 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
val HEALING_MACHINE = blockItem("healing_machine", CobblemonBlocks.HEALING_MACHINE)
@JvmField
val PC = blockItem("pc", CobblemonBlocks.PC)
val TM_MACHINE = blockItem("tm_machine", CobblemonBlocks.TM_MACHINE)
@JvmField
val PASTURE = blockItem("pasture", CobblemonBlocks.PASTURE)
@JvmField
val DISPLAY_CASE = blockItem("display_case", CobblemonBlocks.DISPLAY_CASE)
@JvmField
val MOD_STATION = blockItem("modification_station", CobblemonBlocks.MODIFICATION_STATION)
// Evolution items
@JvmField val LINK_CABLE = create("link_cable", LinkCableItem())
@@ -380,6 +403,18 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val ZINC = create("zinc", VitaminItem(Stats.SPECIAL_DEFENCE))
@JvmField
val GENIUS_FEATHER = create("genius_feather", FeatherItem(Stats.SPECIAL_ATTACK))
@JvmField
val SWIFT_FEATHER = create("swift_feather", FeatherItem(Stats.SPEED))
@JvmField
val HEALTH_FEATHER = create("health_feather", FeatherItem(Stats.HP))
@JvmField
val RESIST_FEATHER = create("resist_feather", FeatherItem(Stats.DEFENCE))
@JvmField
val MUSCLE_FEATHER = create("muscle_feather", FeatherItem(Stats.ATTACK))
@JvmField
val CLEVER_FEATHER = create("clever_feather", FeatherItem(Stats.SPECIAL_DEFENCE))
@JvmField
val MEDICINAL_LEEK = create("medicinal_leek", MedicinalLeekItem(CobblemonBlocks.MEDICINAL_LEEK, Item.Settings().food(FoodComponent.Builder().snack().hunger(1).saturationModifier(0.2f).build())))
@JvmField
val ROASTED_LEEK = create("roasted_leek", Item(Item.Settings().food(FoodComponent.Builder().snack().hunger(3).saturationModifier(0.3f).build())))
@@ -530,6 +565,11 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField val ELIXIR = create("elixir", ElixirItem(max = false))
@JvmField val MAX_ELIXIR = create("max_elixir", ElixirItem(max = true))
@JvmField
val ABILITY_CAPSULE = this.create("ability_capsule", AbilityChangeItem(AbilityChanger.COMMON_ABILITY))
@JvmField
val ABILITY_PATCH = this.create("ability_patch", AbilityChangeItem(AbilityChanger.HIDDEN_ABILITY))
/**
* Evolution Ores and Stones
*/
@@ -733,19 +773,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
@JvmField
val ARMOR_FOSSIL = noSettingsItem("armor_fossil")
@JvmField
val BIRD_FOSSIL = noSettingsItem("bird_fossil")
val FOSSILIZED_BIRD = noSettingsItem("fossilized_bird")
@JvmField
val CLAW_FOSSIL = noSettingsItem("claw_fossil")
@JvmField
val COVER_FOSSIL = noSettingsItem("cover_fossil")
@JvmField
val DINO_FOSSIL = noSettingsItem("dino_fossil")
val FOSSILIZED_DINO = noSettingsItem("fossilized_dino")
@JvmField
val DOME_FOSSIL = noSettingsItem("dome_fossil")
@JvmField
val DRAKE_FOSSIL = noSettingsItem("drake_fossil")
val FOSSILIZED_DRAKE = noSettingsItem("fossilized_drake")
@JvmField
val FISH_FOSSIL = noSettingsItem("fish_fossil")
val FOSSILIZED_FISH = noSettingsItem("fossilized_fish")
@JvmField
val HELIX_FOSSIL = noSettingsItem("helix_fossil")
@JvmField
@@ -824,6 +864,54 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, RegistryKey<Registry<It
// Misc
@JvmField
val POKEMON_MODEL = this.create("pokemon_model", PokemonItem())
@JvmField
val RELIC_COIN = noSettingsItem("relic_coin")
@JvmField
val RELIC_COIN_POUCH = blockItem("relic_coin_pouch", CobblemonBlocks.RELIC_COIN_POUCH)
@JvmField
val RELIC_COIN_SACK = blockItem("relic_coin_sack", CobblemonBlocks.RELIC_COIN_SACK)
@JvmField
val TECHNICAL_MACHINE = this.create("technical_machine", TechnicalMachineItem(Item.Settings()))
@JvmField
val BLANK_TM = this.create("blank_tm", CobblemonItem(Item.Settings()))
// Type Gems
@JvmField
val NORMAL_GEM = noSettingsItem("normal_gem")
@JvmField
val FIRE_GEM = noSettingsItem("fire_gem")
@JvmField
val WATER_GEM = noSettingsItem("water_gem")
@JvmField
val GRASS_GEM = noSettingsItem("grass_gem")
@JvmField
val ELECTRIC_GEM = noSettingsItem("electric_gem")
@JvmField
val ICE_GEM = noSettingsItem("ice_gem")
@JvmField
val FIGHTING_GEM = noSettingsItem("fighting_gem")
@JvmField
val POISON_GEM = noSettingsItem("poison_gem")
@JvmField
val GROUND_GEM = noSettingsItem("ground_gem")
@JvmField
val FLYING_GEM = noSettingsItem("flying_gem")
@JvmField
val PSYCHIC_GEM = noSettingsItem("psychic_gem")
@JvmField
val BUG_GEM = noSettingsItem("bug_gem")
@JvmField
val ROCK_GEM = noSettingsItem("rock_gem")
@JvmField
val GHOST_GEM = noSettingsItem("ghost_gem")
@JvmField
val DRAGON_GEM = noSettingsItem("dragon_gem")
@JvmField
val DARK_GEM = noSettingsItem("dark_gem")
@JvmField
val STEEL_GEM = noSettingsItem("steel_gem")
@JvmField
val FAIRY_GEM = noSettingsItem("fairy_gem")
private fun blockItem(name: String, block: Block): BlockItem = this.create(name, BlockItem(block, Item.Settings()))
@@ -43,6 +43,8 @@ import com.cobblemon.mod.common.client.net.dialogue.DialogueClosedHandler
import com.cobblemon.mod.common.client.net.dialogue.DialogueOpenedHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormEntityParticleHandler
import com.cobblemon.mod.common.client.net.effect.SpawnSnowstormParticleHandler
import com.cobblemon.mod.common.client.net.gui.CraftBlankTMPacketHandler
import com.cobblemon.mod.common.client.net.gui.CraftTMPacketHandler
import com.cobblemon.mod.common.client.net.gui.InteractPokemonUIPacketHandler
import com.cobblemon.mod.common.client.net.gui.SummaryUIPacketHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
@@ -82,9 +84,12 @@ import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPa
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.data.AbilityRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.BerryRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.MovesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.UnlockReloadPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueClosedPacket
import com.cobblemon.mod.common.net.messages.client.dialogue.DialogueOpenedPacket
@@ -128,6 +133,8 @@ import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacke
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeUpdatedPacket
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.SummaryUIPacket
import com.cobblemon.mod.common.net.messages.server.*
@@ -248,6 +255,7 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PokemonStateUpdatePacket.ID, PokemonStateUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TetheringUpdatePacket.ID, TetheringUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(TradeableUpdatePacket.ID, TradeableUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(SpeciesFeatureUpdatePacket.ID, SpeciesFeatureUpdatePacket::decode, PokemonUpdatePacketHandler())
this.createClientBound(OriginalTrainerUpdatePacket.ID, OriginalTrainerUpdatePacket::decode, PokemonUpdatePacketHandler())
// Evolution start
@@ -314,6 +322,9 @@ object CobblemonNetwork : NetworkManager {
this.createClientBound(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(UnlockReloadPacket.ID, UnlockReloadPacket::decode, UnlockReloadPacketHandler)
this.createClientBound(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(SpeciesFeatureAssignmentSyncPacket.ID, SpeciesFeatureAssignmentSyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(NaturalMaterialRegistrySyncPacket.ID, NaturalMaterialRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
this.createClientBound(FossilRegistrySyncPacket.ID, FossilRegistrySyncPacket::decode, DataRegistrySyncPacketHandler())
@@ -425,6 +436,10 @@ object CobblemonNetwork : NetworkManager {
this.createServerBound(PartyPokemonMoveSelectedPacket.ID, PartyPokemonMoveSelectedPacket::decode, PartyPokemonMoveSelectedHandler)
this.createServerBound(PartyMoveSelectCancelledPacket.ID, PartyMoveSelectCancelledPacket::decode, PartyMoveSelectCancelledHandler)
// TM Machine packets
this.createServerBound(CraftTMPacket.ID, CraftTMPacket::decode, CraftTMPacketHandler)
this.createServerBound(CraftBlankTMPacket.ID, CraftBlankTMPacket::decode, CraftBlankTMPacketHandler)
// Dialogue packets
this.createServerBound(EscapeDialoguePacket.ID, EscapeDialoguePacket::decode, EscapeDialogueHandler)
this.createServerBound(InputToDialoguePacket.ID, InputToDialoguePacket::decode, InputToDialogueHandler)
@@ -16,6 +16,7 @@ import net.minecraft.registry.RegistryKey
import net.minecraft.registry.RegistryKeys
import net.minecraft.sound.BlockSoundGroup
import net.minecraft.sound.SoundEvent
import net.minecraft.sound.SoundEvents
object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Registry<SoundEvent>>, SoundEvent>() {
@@ -84,6 +85,8 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
val MEDICINE_PILLS_USE = this.create("medicine_pills.use")
@JvmField
val MEDICINE_SPRAY_USE = this.create("medicine_spray.use")
@JvmField
val MEDICINE_FEATHER_USE = this.create("medicine_feather.use")
@JvmField
val BERRY_HARVEST = this.create("berry.harvest")
@@ -118,6 +121,14 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val FOSSIL_MACHINE_UNPROTECTED = this.create("fossilmachine.unprotected")
@JvmField
val COIN_POUCH_BREAK = this.create("coin_pouch.break")
@JvmField
val COIN_POUCH_HIT = this.create("coin_pouch.hit")
@JvmField
val COIN_POUCH_STEP = this.create("coin_pouch.step")
@JvmField
val COIN_POUCH_PLACE = this.create("coin_pouch.place")
@JvmField
val TUMBLESTONE_BREAK = this.create("tumblestone.break")
@JvmField
@@ -129,6 +140,52 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
@JvmField
val TUMBLESTONE_STEP = this.create("tumblestone.step")
@JvmField
val GIMMIGHOUL_GIVE_ITEM_SMALL = this.create("gimmighoul.give_item_small")
@JvmField
val GIMMIGHOUL_REVEAL = this.create("gimmighoul.reveal")
@JvmField
val BERRY_BUSH_BREAK = this.create("berry_bush.break")
@JvmField
val BERRY_BUSH_PLACE = this.create("berry_bush.place")
@JvmField
val BIG_ROOT_BREAK = this.create("big_root.break")
@JvmField
val ENERGY_ROOT_PLACE = this.create("energy_root.place")
@JvmField
val MINT_BREAK = this.create("mint.break")
@JvmField
val MINT_PLACE = this.create("mint.place")
@JvmField
val MEDICINAL_LEEK_BREAK = this.create("medicinal_leek.break")
@JvmField
val MEDICINAL_LEEK_PLACE = this.create("medicinal_leek.plant")
@JvmField
val GILDED_CHEST_OPEN = this.create("gilded_chest.open")
@JvmField
val GILDED_CHEST_CLOSE = this.create("gilded_chest.close")
@JvmField
val GILDED_CHEST_STEP = this.create("gilded_chest.step")
@JvmField
val GILDED_CHEST_HIT = this.create("gilded_chest.hit")
@JvmField
val GILDED_CHEST_BREAK = this.create("gilded_chest.break")
@JvmField
val GILDED_CHEST_PLACE = this.create("gilded_chest.place")
@JvmField
val COIN_POUCH_SOUNDS = BlockSoundGroup(1f, 1f,
COIN_POUCH_BREAK,
COIN_POUCH_STEP,
COIN_POUCH_PLACE,
COIN_POUCH_HIT,
COIN_POUCH_STEP
)
@JvmField
val IMPACT_NORMAL = this.create("impact.normal")
@JvmField
@@ -183,6 +240,95 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, RegistryKey<Regi
TUMBLESTONE_HIT,
TUMBLESTONE_STEP
)
@JvmField
val TM_USE = this.create("tm.use")
@JvmField
val TMM_CRAFT = this.create("tmm.craft")
@JvmField
val TMM_CRAFT_BLANK = this.create("tmm.craft_blank")
@JvmField
val TMM_OFF = this.create("tmm.off")
@JvmField
val TMM_ON = this.create("tmm.on")
@JvmField
val TYPE_GEM_SOUNDS = TUMBLESTONE_BLOCK_SOUNDS
@JvmField
val BERRY_BUSH_SOUNDS = BlockSoundGroup(1f, 1f,
BERRY_BUSH_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
BERRY_BUSH_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_STEP
)
@JvmField
val BIG_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
BIG_ROOT_BREAK,
SoundEvents.BLOCK_ROOTS_STEP,
SoundEvents.BLOCK_ROOTS_PLACE,
SoundEvents.BLOCK_ROOTS_HIT,
SoundEvents.BLOCK_ROOTS_FALL
)
@JvmField
val ENERGY_ROOT_SOUNDS = BlockSoundGroup(1f, 1f,
SoundEvents.BLOCK_ROOTS_BREAK,
SoundEvents.BLOCK_ROOTS_STEP,
ENERGY_ROOT_PLACE,
SoundEvents.BLOCK_ROOTS_HIT,
SoundEvents.BLOCK_ROOTS_FALL
)
@JvmField
val MEDICINAL_LEEK_SOUNDS = BlockSoundGroup(1f, 1f,
MEDICINAL_LEEK_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
MEDICINAL_LEEK_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val MINT_SOUNDS = BlockSoundGroup(1f, 1f,
MINT_BREAK,
SoundEvents.BLOCK_GRASS_STEP,
MINT_PLACE,
SoundEvents.BLOCK_GRASS_HIT,
SoundEvents.BLOCK_GRASS_FALL
)
@JvmField
val GILDED_CHEST_SOUNDS = BlockSoundGroup(1f, 1f,
GILDED_CHEST_BREAK,
GILDED_CHEST_STEP,
GILDED_CHEST_PLACE,
GILDED_CHEST_HIT,
GILDED_CHEST_STEP
)
@JvmField
val DISPLAY_CASE_ADD_ITEM = this.create("display_case.add_item")
@JvmField
val DISPLAY_CASE_REMOVE_ITEM = this.create("display_case.remove_item")
@JvmField
val DISPLAY_CASE_BREAK = this.create("display_case.break")
@JvmField
val DISPLAY_CASE_HIT = this.create("display_case.hit")
@JvmField
val DISPLAY_CASE_PLACE = this.create("display_case.place")
@JvmField
val DISPLAY_CASE_STEP = this.create("display_case.step")
@JvmField
val DISPLAY_CASE_SOUNDS = BlockSoundGroup(1f, 1f,
DISPLAY_CASE_BREAK,
DISPLAY_CASE_STEP,
DISPLAY_CASE_PLACE,
DISPLAY_CASE_HIT,
DISPLAY_CASE_STEP
)
private fun create(name: String): SoundEvent = this.create(name, SoundEvent.of(cobblemonResource(name)))
}
@@ -113,6 +113,14 @@ object CobblemonCriteria {
)
)
// Advancement criteria for [grow_tumblestone.json]
val PLANT_TUMBLESTONE = this.create(
SimpleCriterionTrigger(
cobblemonResource("plant_tumblestone"),
PlantTumblestoneCriterionCondition::class.java
)
)
private fun <T : Criterion<*>> create(criteria: T): T = Cobblemon.implementation.registerCriteria(criteria)
}
@@ -0,0 +1,52 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.advancement.criterion
import com.cobblemon.mod.common.block.TumblestoneBlock
import com.google.gson.JsonObject
import net.minecraft.predicate.entity.LootContextPredicate
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.BlockPos
/**
* A criterion condition for when a tumblestone block is planted
* @param id The identifier of the criterion
* @param predicate The predicate for the criterion
*
* @author Aethen
* @since 02/28/2024
*/
class PlantTumblestoneCriterionCondition(id: Identifier, predicate: LootContextPredicate) :
SimpleCriterionCondition<PlantTumblestoneContext>(id, predicate) {
override fun toJson(json: JsonObject) {
// Add properties to json if needed. None needed for this criterion
}
override fun fromJson(json: JsonObject) {
// Parse properties from json if needed. None needed for this criterion
}
/**
* Checks if the tumblestone block can grow at the given position
* @param player The player that planted the tumblestone block
* @param context The context of the criterion
* @return True if the tumblestone block can grow at the given position, false otherwise
*/
override fun matches(player: ServerPlayerEntity, context: PlantTumblestoneContext): Boolean {
return context.tumbleStoneBlock.canGrow(context.pos, player.world)
}
}
/**
* The context of the [PlantTumblestoneCriterionCondition]
* @param pos The position of the tumblestone block
* @param tumbleStoneBlock The tumblestone block
*/
open class PlantTumblestoneContext(var pos: BlockPos, var tumbleStoneBlock: TumblestoneBlock)
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.abilities
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
@@ -21,7 +22,7 @@ import net.minecraft.nbt.NbtCompound
* @author Qu
* @since January 9th, 2022
*/
open class Ability internal constructor(var template: AbilityTemplate, var forced: Boolean) {
open class Ability internal constructor(var template: AbilityTemplate, forced: Boolean) {
val name: String
get() = template.name
@@ -34,16 +35,26 @@ open class Ability internal constructor(var template: AbilityTemplate, var force
/**
* This represents the last known index of this backing ability in the species data.
* If -1 the ability will attempt to find the correct index or reroll itself, this is done in order to be compatible with existing data
* The value can be mutated however this is not recommended by API users.
* @see [Pokemon.updateAbility].
*/
var forced: Boolean = forced
internal set
/**
* This represents the last known index of this backing ability in the species data.
*
* @see [Pokemon.updateAbility].
*/
var index: Int = -1
internal set
/**
* The last known priority of this ability in the species data.
* The value can be mutated however this is not recommended by API users.
*
* @see [Pokemon.updateAbility].
*/
var priority = Priority.LOWEST
internal set
open fun saveToNBT(nbt: NbtCompound): NbtCompound {
nbt.putString(DataKeys.POKEMON_ABILITY_NAME, name)
@@ -62,19 +73,23 @@ open class Ability internal constructor(var template: AbilityTemplate, var force
}
open fun loadFromNBT(nbt: NbtCompound): Ability {
forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
this.template = Abilities.getOrException(nbt.getString(DataKeys.POKEMON_ABILITY_NAME))
this.forced = nbt.getBoolean(DataKeys.POKEMON_ABILITY_FORCED)
if (nbt.contains(DataKeys.POKEMON_ABILITY_INDEX) && nbt.contains(DataKeys.POKEMON_ABILITY_PRIORITY)) {
this.index = nbt.getInt(DataKeys.POKEMON_ABILITY_INDEX)
this.priority = Priority.valueOf(nbt.getString(DataKeys.POKEMON_ABILITY_PRIORITY))
}
return this
}
open fun loadFromJSON(json: JsonObject): Ability {
forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
this.template = Abilities.getOrException(json.get(DataKeys.POKEMON_ABILITY_NAME).asString)
this.forced = json.get(DataKeys.POKEMON_ABILITY_FORCED)?.asBoolean ?: false
if (json.has(DataKeys.POKEMON_ABILITY_INDEX) && json.has(DataKeys.POKEMON_ABILITY_PRIORITY)) {
this.index = json.get(DataKeys.POKEMON_ABILITY_INDEX).asInt
this.priority = Priority.valueOf(json.get(DataKeys.POKEMON_ABILITY_PRIORITY).asString)
}
return this
}
}
@@ -28,7 +28,6 @@ class AbilityTemplate(
*/
fun create(forced: Boolean = false) = builder(this, forced)
/**
* Returns the Ability and loads the given NBT Tag into it.
*
@@ -42,4 +41,5 @@ class AbilityTemplate(
* Ability extensions need to write and read their needed data from here.
*/
fun create(json: JsonObject) = create().loadFromJSON(json)
}
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.drop
import com.cobblemon.mod.common.api.events.CobblemonEvents.LOOT_DROPPED
import com.cobblemon.mod.common.api.events.drops.LootDroppedEvent
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.entity.LivingEntity
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
@@ -88,6 +89,8 @@ class DropTable {
amount: IntRange = this.amount
) {
val drops = getDrops(amount).toMutableList()
val heldItem = (entity as PokemonEntity).pokemon.heldItemNoCopy()
if (!heldItem.isEmpty) entity.dropItem(heldItem.item)
LOOT_DROPPED.postThen(
event = LootDroppedEvent(this, player, entity, drops),
ifSucceeded = { it.drops.forEach { it.drop(entity, world, pos, player) } }
@@ -33,6 +33,7 @@ import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionCompleteEv
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionDisplayEvent
import com.cobblemon.mod.common.api.events.pokemon.evolution.EvolutionTestedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.ExperienceCandyUseEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.HeldItemUpdatedEvent
import com.cobblemon.mod.common.api.events.pokemon.interaction.PokemonInteractionGUICreationEvent
import com.cobblemon.mod.common.api.events.starter.StarterChosenEvent
import com.cobblemon.mod.common.api.events.storage.ReleasePokemonEvent
@@ -66,6 +67,8 @@ object CobblemonEvents {
val EVOLUTION_COMPLETE = EventObservable<EvolutionCompleteEvent>()
@JvmField
val POKEMON_NICKNAMED = CancelableObservable<PokemonNicknamedEvent>()
@JvmField
val HELD_ITEM_UPDATED = CancelableObservable<HeldItemUpdatedEvent>()
@JvmField
val THROWN_POKEBALL_HIT = CancelableObservable<ThrownPokeballHitEvent>()
@@ -0,0 +1,36 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.events.pokemon.interaction
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.entity.LivingEntity
import net.minecraft.item.ItemStack
/**
* Event published when a held item is being updated on a [Pokemon].
* Cancelling the event will prevent any action from being taken on
* the Pokémon.
*
* [newItem] differs from [originalStack] in that the former is a
* one-sized version of the original (Pokémon cannot hold stacks) and
* changing it in any way will not affect the original item held by
* the player.
*
* @author Whatsy, Hiroku
* @since November 13th, 2023
*/
data class HeldItemUpdatedEvent(
val cause: LivingEntity?,
val pokemon: Pokemon,
val originalStack: ItemStack,
val decrement: Boolean,
val oldItem: ItemStack,
val newItem: ItemStack
) : Cancelable()
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.item.ability
import com.cobblemon.mod.common.api.abilities.*
import com.cobblemon.mod.common.item.interactive.ability.AbilityTypeChanger
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbility
import com.cobblemon.mod.common.pokemon.abilities.HiddenAbilityType
/**
* Represents a change operation for a [Pokemon.ability].
* This is backed by the associated [PotentialAbility].
*
* @param T The type of [PotentialAbility].
*/
interface AbilityChanger<T : PotentialAbility> {
/**
* The [PotentialAbilityType] of type [T].
*/
val type: PotentialAbilityType<T>
/**
* Collects all the possible targets for the [type] and the given [pokemon].
*
* @param pokemon The [Pokemon] being queried.
* @return A set containing all their legal abilities associated with [type], can be empty.
*/
fun queryPossible(pokemon: Pokemon): Set<AbilityTemplate>
/**
* Attempts to update the ability of the given [pokemon].
*
* @param pokemon The [Pokemon] attempting to change an ability.
* @return If the operation was successful.
*/
fun performChange(pokemon: Pokemon): Boolean
/**
* Checks if the current [PotentialAbilityType] of a [Pokemon.ability] is possible to swap from with this.
*
* @param type The [PotentialAbilityType] or null if the ability is forced and/or illegal.
* @return If the operation can be performed.
*/
fun canChangeFrom(type: PotentialAbilityType<*>?): Boolean
companion object {
/**
* An implementation of [AbilityChanger] for [CommonAbility].
*
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 2 common abilities.
*/
@JvmStatic
val COMMON_ABILITY: AbilityChanger<CommonAbility> = AbilityTypeChanger(CommonAbilityType) { other -> other == CommonAbilityType }
/**
* An implementation of [AbilityChanger] for [HiddenAbility].
*
* While this can be used in custom data scenarios this functions most "game-like" if the Pokémon only has 1 hidden ability.
*/
@JvmStatic
val HIDDEN_ABILITY: AbilityChanger<HiddenAbility> = AbilityTypeChanger(HiddenAbilityType) { other -> other == CommonAbilityType || other == HiddenAbilityType }
}
}
@@ -19,6 +19,7 @@ import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
import net.minecraft.world.World
/**
@@ -77,6 +78,11 @@ abstract class MultiblockBlock(properties: Settings) : BlockWithEntity(propertie
return BlockRenderType.MODEL
}
override fun getPickStack(world: BlockView, pos: BlockPos, state: BlockState): ItemStack {
val blockEntity = world.getBlockEntity(pos) as? MultiblockEntity ?: return ItemStack.EMPTY
return if (blockEntity.multiblockStructure == null) super.getPickStack(world, pos, state) else ItemStack.EMPTY
}
abstract fun createMultiBlockEntity(pos: BlockPos, state: BlockState): FossilMultiblockEntity
}
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.abilities.Ability
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
@@ -294,6 +295,18 @@ open class PokemonProperties {
)
fun apply(pokemon: Pokemon) {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
this.customProperties.forEach { it.apply(pokemon) }
this.commonApply(pokemon)
}
fun apply(pokemonEntity: PokemonEntity) {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
this.customProperties.forEach { it.apply(pokemonEntity) }
this.commonApply(pokemonEntity.pokemon)
}
private fun commonApply(pokemon: Pokemon) {
species?.let {
return@let try {
if (it == "random") {
@@ -313,8 +326,8 @@ open class PokemonProperties {
friendship?.let { pokemon.setFriendship(it) }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemon.caughtBall = pokeball } }
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemon.nature = nature } }
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemon.ability = ability } }
status?.let { Statuses.getStatus(it)?.let { pokemon.applyStatus(it as PersistentStatus) } }
ability?.let { this.createAbility(it, pokemon.form)?.let(pokemon::updateAbility) }
status?.let { Statuses.getStatus(it)?.let { status -> if (status is PersistentStatus) pokemon.applyStatus(status) } }
customProperties.forEach { it.apply(pokemon) }
ivs?.let { ivs ->
ivs.forEach { stat ->
@@ -355,69 +368,17 @@ open class PokemonProperties {
pokemon.updateAspects()
}
fun apply(pokemonEntity: PokemonEntity) {
species?.let {
return@let try {
if (it == "random") {
PokemonSpecies.species.random()
} else {
PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace())
}
} catch (e: InvalidIdentifierException) {
null
}
}?.let { pokemonEntity.pokemon.species = it }
nickname?.let { pokemonEntity.pokemon.nickname = it }
form?.let { formID -> pokemonEntity.pokemon.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formID, true) } }?.let { form -> pokemonEntity.pokemon.form = form }
level?.let { pokemonEntity.pokemon.level = it }
shiny?.let { pokemonEntity.pokemon.shiny = it }
gender?.let { pokemonEntity.pokemon.gender = it }
friendship?.let { pokemonEntity.pokemon.setFriendship(it) }
pokeball?.let { PokeBalls.getPokeBall(it.asIdentifierDefaultingNamespace())?.let { pokeball -> pokemonEntity.pokemon.caughtBall = pokeball } }
nature?.let { Natures.getNature(it.asIdentifierDefaultingNamespace())?.let { nature -> pokemonEntity.pokemon.nature = nature } }
ability?.let { Abilities.getOrException(it).create(true).let { ability -> pokemonEntity.pokemon.ability = ability } }
status?.let { Statuses.getStatus(it)?.let { pokemonEntity.pokemon.applyStatus(it as PersistentStatus) } }
customProperties.forEach { it.apply(pokemonEntity) }
ivs?.let { ivs ->
ivs.forEach { stat ->
pokemonEntity.pokemon.setIV(stat.key, stat.value)
}
}
evs?.let { evs ->
evs.forEach { stat ->
pokemonEntity.pokemon.setEV(stat.key, stat.value)
}
}
teraType?.let { ElementalTypes.get(it)?.let { type -> pokemonEntity.pokemon.teraType = type } }
dmaxLevel?.let { pokemonEntity.pokemon.dmaxLevel = it }
gmaxFactor?.let { pokemonEntity.pokemon.gmaxFactor = it }
tradeable?.let { pokemonEntity.pokemon.tradeable = it }
// If it has been explicitly set to NONE, clear the stored information, including the originalTrainer property
originalTrainerType?.let {
if (it == OriginalTrainerType.NONE) {
pokemonEntity.pokemon.removeOriginalTrainer()
originalTrainer = null
}
}
originalTrainer?.let { ot ->
val type = originalTrainerType ?: pokemonEntity.pokemon.originalTrainerType
when (type) {
OriginalTrainerType.PLAYER -> {
when (ot.length) {
in 3..16 -> server()?.userCache?.findByName(ot)?.get()?.id // OT is a Username
36 -> UUID.fromString(ot) // OT is a UUID
else -> null // OT is invalid
}?.let { uuid -> pokemonEntity.pokemon.setOriginalTrainer(uuid) }
}
OriginalTrainerType.NPC -> pokemonEntity.pokemon.setOriginalTrainer(ot)
else -> {}
}
pokemonEntity.pokemon.refreshOriginalTrainer()
}
pokemonEntity.pokemon.updateAspects()
fun matches(pokemon: Pokemon): Boolean {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
return this.commonMatches(pokemon) && this.customProperties.none { !it.matches(pokemon) }
}
fun matches(pokemon: Pokemon): Boolean {
fun matches(pokemonEntity: PokemonEntity): Boolean {
// Custom properties check could be duplicated but can't assume 3rd party hasn't done anything type specific
return this.commonMatches(pokemonEntity.pokemon) && this.customProperties.none { !it.matches(pokemonEntity) }
}
private fun commonMatches(pokemon: Pokemon): Boolean {
level?.takeIf { it != pokemon.level }?.let { return false }
shiny?.takeIf { it != pokemon.shiny }?.let { return false }
gender?.takeIf { it != pokemon.gender }?.let { return false }
@@ -454,53 +415,17 @@ open class PokemonProperties {
tradeable?.takeIf { it != pokemon.tradeable }?.let { return false }
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemon) }
}
fun matches(pokemonEntity: PokemonEntity): Boolean {
level?.takeIf { it != pokemonEntity.pokemon.level }?.let { return false }
shiny?.takeIf { it != ("shiny" in pokemonEntity.pokemon.aspects) }?.let { return false }
gender?.takeIf { it != pokemonEntity.pokemon.gender }?.let { return false }
species?.run {
try {
val species = if (this == "random") {
PokemonSpecies.species.random()
} else {
PokemonSpecies.getByIdentifier(this.asIdentifierDefaultingNamespace()) ?: return@run
}
if (pokemonEntity.pokemon.species != species) {
return false
}
} catch (e: InvalidIdentifierException) {}
}
nickname?.takeIf { it != pokemonEntity.pokemon.nickname }?.let { return false }
form?.takeIf { !it.equals(pokemonEntity.pokemon.form.name, true) }?.let { return false }
friendship?.takeIf { it != pokemonEntity.pokemon.friendship }?.let { return false }
pokeball?.takeIf { it != pokemonEntity.pokemon.caughtBall.name.toString() }?.let { return false }
nature?.takeIf { it != pokemonEntity.pokemon.nature.name.toString() }?.let { return false }
ability?.takeIf { it != pokemonEntity.pokemon.ability.name }?.let { return false }
status?.takeIf { it != pokemonEntity.pokemon.status?.status?.showdownName }?.let { return false }
ivs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.ivs[stat.key]) { return false }
}
evs?.forEach{ stat ->
if (stat.value != pokemonEntity.pokemon.evs[stat.key]) { return false }
}
teraType?.takeIf { it != pokemonEntity.pokemon.teraType.name }?.let { return false }
dmaxLevel?.takeIf { it != pokemonEntity.pokemon.dmaxLevel }?.let { return false }
gmaxFactor?.takeIf { it != pokemonEntity.pokemon.gmaxFactor }?.let { return false }
tradeable?.takeIf { it != pokemonEntity.pokemon.tradeable }?.let { return false }
originalTrainer?.takeIf { it != pokemonEntity.pokemon.originalTrainer }?.let{ return false }
originalTrainerType?.takeIf { it != pokemonEntity.pokemon.originalTrainerType }?.let{ return false }
return customProperties.none { !it.matches(pokemonEntity) }
return true
}
// TODO Review the need for this, usage for us is in commands that deviate from the behavior established by multiple Pokémon query equivalent
fun isSubSetOf(properties: PokemonProperties): Boolean {
level?.takeIf { it != properties.level }?.let { return false }
shiny?.takeIf { it != ("shiny" in properties.aspects) }?.let { return false }
gender?.takeIf { it != properties.gender }?.let { return false }
species?.run {
try {
// TODO get context on this bit, it's highly unlikely two randoms would result in the same Pokémon, doesn't mean the prop is not equal?
val species = if (this == "random") {
PokemonSpecies.species.random()
} else {
@@ -509,7 +434,7 @@ open class PokemonProperties {
if (properties.species != species.toString()) {
return false
}
} catch (e: InvalidIdentifierException) {}
} catch (_: InvalidIdentifierException) {}
}
nickname?.takeIf { it != properties.nickname }?.let { return false }
form?.takeIf { !it.equals(properties.form, true) }?.let { return false }
@@ -558,13 +483,11 @@ open class PokemonProperties {
else baseTypes.random()
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
fun createEntity(world: World): PokemonEntity {
return PokemonEntity(world, create())
}
// TODO Codecs at some point
fun saveToNBT(): NbtCompound {
val nbt = NbtCompound()
originalString.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
@@ -593,6 +516,7 @@ open class PokemonProperties {
return nbt
}
// TODO Codecs at some point
fun loadFromNBT(tag: NbtCompound): PokemonProperties {
originalString = tag.getString(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)
level = if (tag.contains(DataKeys.POKEMON_LEVEL)) tag.getInt(DataKeys.POKEMON_LEVEL) else null
@@ -621,6 +545,7 @@ open class PokemonProperties {
return this
}
// TODO Codecs at some point
fun saveToJSON(): JsonObject {
val json = JsonObject()
originalString.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT, it) }
@@ -650,6 +575,7 @@ open class PokemonProperties {
return json
}
// TODO Codecs at some point
fun loadFromJSON(json: JsonObject): PokemonProperties {
originalString = json.get(DataKeys.POKEMON_PROPERTIES_ORIGINAL_TEXT)?.asString ?: ""
level = json.get(DataKeys.POKEMON_LEVEL)?.asInt
@@ -727,4 +653,21 @@ open class PokemonProperties {
fun copy(): PokemonProperties {
return PokemonProperties().loadFromJSON(saveToJSON())
}
// If the config value is at least 1, then do 1/x and use that as the property chance
private fun Float.checkRate(): Boolean = this >= 1 && (Random.Default.nextFloat() < 1 / this)
/**
* Attempts to find an ability by ID and resolve if it should be forced or if it's legal for the given form.
*
* @param id The literal ID of the [Ability] being mapped.
* @param form The [FormData] whose [FormData.abilities] is queried for legality.
* @return The [Ability] with the [Ability.forced] state necessary for the given [form].
*/
private fun createAbility(id: String, form: FormData): Ability? {
val ability = Abilities.get(id) ?: return null
val potentialAbility = form.abilities.firstOrNull { potential -> potential.template == ability } ?: return ability.create(true)
return potentialAbility.template.create(false)
}
}
@@ -34,4 +34,10 @@ interface PassiveEvolution : Evolution {
return false
}
/**
* If the evolution will disappear once the conditions are no longer met.
*/
val permanent: Boolean
}
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.substitute
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A [SpeciesFeatureProvider] which is a string value selected from a fixed list of choices. Parameters exist
@@ -24,15 +26,46 @@ import net.minecraft.nbt.NbtCompound
* @since November 30th, 2022
*/
open class ChoiceSpeciesFeatureProvider(
override val keys: List<String>,
override var keys: List<String>,
var default: String? = null,
var choices: List<String> = listOf(),
var isAspect: Boolean = true,
var aspectFormat: String = "{{choice}}"
) : SpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
override val needsKey = true
) : SynchronizedSpeciesFeatureProvider<StringSpeciesFeature>, CustomPokemonPropertyType<StringSpeciesFeature>, AspectProvider {
override var needsKey = true
override var visible = false
fun getAspect(feature: StringSpeciesFeature) = aspectFormat.substitute("choice", feature.value)
override fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
buffer.writeCollection(choices) { _, value -> buffer.writeString(value) }
buffer.writeBoolean(isAspect)
buffer.writeString(aspectFormat)
buffer.writeBoolean(needsKey)
}
override fun decode(buffer: PacketByteBuf) {
keys = buffer.readList { buffer.readString() }
default = buffer.readNullable { buffer.readString() }
choices = buffer.readList { buffer.readString() }
isAspect = buffer.readBoolean()
aspectFormat = buffer.readString()
needsKey = buffer.readBoolean()
}
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<StringSpeciesFeature>? {
return null
}
override fun invoke(buffer: PacketByteBuf, name: String): StringSpeciesFeature? {
return if (name in keys) {
StringSpeciesFeature(name, "").also { it.decode(buffer) }
} else {
null
}
}
fun getAllAspects(): MutableList<String> {
val aspects = choices.toMutableList()
choices.forEach {
@@ -45,10 +78,10 @@ open class ChoiceSpeciesFeatureProvider(
internal constructor(): this(emptyList())
fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
override fun get(pokemon: Pokemon) = pokemon.getFeature<StringSpeciesFeature>(keys.first())
override fun invoke(pokemon: Pokemon): StringSpeciesFeature? {
val existing = pokemon.getFeature<StringSpeciesFeature>(keys.first())
val existing = get(pokemon)
return if (existing != null && existing.value in choices) {
existing
} else {
@@ -88,7 +121,7 @@ open class ChoiceSpeciesFeatureProvider(
override fun provide(pokemon: Pokemon): Set<String> {
return if (isAspect) {
pokemon.getFeature<StringSpeciesFeature>(keys.first())?.let { setOf(getAspect(it)) } ?: emptySet()
get(pokemon)?.let { setOf(getAspect(it)) } ?: emptySet()
} else {
emptySet()
}
@@ -12,10 +12,12 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import kotlin.random.Random
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A simple [SpeciesFeature] that is a true/false flag value. It implements [CustomPokemonProperty] for use in
@@ -26,7 +28,7 @@ import net.minecraft.nbt.NbtCompound
* @author Hiroku
* @since May 13th, 2022
*/
open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, CustomPokemonProperty {
open class FlagSpeciesFeature(override val name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
constructor(name: String, enabled: Boolean): this(name) {
this.enabled = enabled
}
@@ -53,6 +55,14 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
return this
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeBoolean(enabled)
}
override fun decode(buffer: PacketByteBuf) {
enabled = buffer.readBoolean()
}
override fun asString() = "$name=$enabled"
override fun apply(pokemon: Pokemon) {
@@ -71,11 +81,37 @@ open class FlagSpeciesFeature(override val name: String) : SpeciesFeature, Custo
override fun matches(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(name)?.enabled == enabled
}
class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
override val keys: List<String>
class FlagSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<FlagSpeciesFeature>, CustomPokemonPropertyType<FlagSpeciesFeature>, AspectProvider {
override var keys: List<String> = emptyList()
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
override val needsKey get() = true
var default: String? = null
val isAspect = true
var isAspect = true
override var visible: Boolean = false
override fun invoke(buffer: PacketByteBuf, name: String): FlagSpeciesFeature? {
return if (name in keys) {
FlagSpeciesFeature(name).also { it.decode(buffer) }
} else {
null
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
buffer.writeNullable(default) { _, value -> buffer.writeString(value) }
buffer.writeBoolean(isAspect)
}
override fun decode(buffer: PacketByteBuf) {
keys = buffer.readList { it.readString() }
default = buffer.readNullable { it.readString() }
isAspect = buffer.readBoolean()
}
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<FlagSpeciesFeature>? {
return null
}
override fun examples() = setOf("true", "false")
@@ -94,8 +130,10 @@ class FlagSpeciesFeatureProvider : SpeciesFeatureProvider<FlagSpeciesFeature>, C
constructor(vararg keys: String) : this(keys.toList())
override fun get(pokemon: Pokemon) = pokemon.getFeature<FlagSpeciesFeature>(keys.first())
override fun invoke(pokemon: Pokemon): FlagSpeciesFeature? {
return pokemon.getFeature(keys.first())
return get(pokemon)
?: when (default) {
"random" -> FlagSpeciesFeature(keys.first(), Random.Default.nextBoolean())
in setOf("true", "false") -> FlagSpeciesFeature(keys.first(), default.toBoolean())
@@ -8,13 +8,17 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.GlobalSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -24,6 +28,7 @@ import net.minecraft.nbt.NbtCompound
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* A registry very similar to [SpeciesFeatures] but the [SpeciesFeatureProvider] contained within it
@@ -42,11 +47,15 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.create()
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
override val resourcePath: String = "global_species_features"
override fun sync(player: ServerPlayerEntity) {}
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(GlobalSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
}
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
resourceFeatures.keys.toList().forEach(this::unregister)
@@ -59,6 +68,10 @@ object GlobalSpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
codeFeatures.putAll(entries.map { it.toPair() })
}
private fun register(name: String, provider: SpeciesFeatureProvider<*>, isCoded: Boolean) {
val mapping = if (isCoded) codeFeatures else resourceFeatures
if (provider is AspectProvider) {
@@ -0,0 +1,181 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.BarSummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.JsonObject
import com.google.gson.annotations.SerializedName
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* A species feature value that's just an integer. Complex stuff.
* @author Hiroku
*/
class IntSpeciesFeature(override var name: String) : SynchronizedSpeciesFeature, CustomPokemonProperty {
var value = 0
constructor(): this("")
constructor(name: String, value: Int): this(name) {
this.value = value
}
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
pokemonNBT.putInt(name, value)
return pokemonNBT
}
override fun loadFromNBT(pokemonNBT: NbtCompound): SynchronizedSpeciesFeature {
value = pokemonNBT.getInt(name)
return this
}
override fun saveToJSON(pokemonJSON: JsonObject): JsonObject {
pokemonJSON.addProperty(name, value)
return pokemonJSON
}
override fun loadFromJSON(pokemonJSON: JsonObject): SpeciesFeature {
value = pokemonJSON.get(name).asInt
return this
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeInt(value)
}
override fun decode(buffer: PacketByteBuf) {
value = buffer.readInt()
}
override fun asString() = "$name=$value"
override fun apply(pokemon: Pokemon) {
val featureProvider = SpeciesFeatures.getFeature(name) ?: return
if (featureProvider in SpeciesFeatures.getFeaturesFor(pokemon.species)) {
val existingFeature = pokemon.getFeature<IntSpeciesFeature>(name)
if (existingFeature != null) {
existingFeature.value = value
} else {
pokemon.features.add(IntSpeciesFeature(name, value))
}
pokemon.updateAspects()
}
}
override fun matches(pokemon: Pokemon) = pokemon.getFeature<IntSpeciesFeature>(name)?.value == value
}
class IntSpeciesFeatureProvider : SynchronizedSpeciesFeatureProvider<IntSpeciesFeature>, CustomPokemonPropertyType<IntSpeciesFeature> {
class DisplayData : Encodable, Decodable {
var name: String = ""
@SerializedName(value = "colour" /* fuck you we use real english */, alternate = ["color"])
var colour = Vec3d(255.0, 255.0, 255.0)
var underlay: Identifier? = null
var overlay: Identifier? = null
override fun decode(buffer: PacketByteBuf) {
name = buffer.readString()
colour = Vec3d(buffer.readDouble(), buffer.readDouble(), buffer.readDouble())
underlay = buffer.readNullable { buffer.readIdentifier() }
overlay = buffer.readNullable { buffer.readIdentifier() }
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(name)
buffer.writeDouble(colour.x)
buffer.writeDouble(colour.y)
buffer.writeDouble(colour.z)
buffer.writeNullable(underlay) { _, value -> buffer.writeIdentifier(value) }
buffer.writeNullable(overlay) { _, value -> buffer.writeIdentifier(value) }
}
}
override var keys = listOf<String>()
// Uses get() = true because that way there's no backing field. It MUST be true, this way no JSON trickery will overwrite it
override val needsKey get() = true
override var visible = false
var default: Int? = null
var min = 0
var max = 100
var display: DisplayData? = null
override fun fromString(value: String?) = value?.toIntOrNull()?.takeIf { it in min..max }?.let { IntSpeciesFeature(keys.first(), it) }
override fun examples() = emptyList<String>()
override fun invoke(buffer: PacketByteBuf, name: String): IntSpeciesFeature? {
return if (name in keys) {
IntSpeciesFeature(name, buffer.readInt())
} else {
null
}
}
override fun invoke(pokemon: Pokemon): IntSpeciesFeature? {
return get(pokemon) ?: default?.let { IntSpeciesFeature(keys.first(), it) }
}
override fun invoke(nbt: NbtCompound): IntSpeciesFeature? {
return if (nbt.contains(keys.first())) {
IntSpeciesFeature(keys.first(), nbt.getInt(keys.first()))
} else {
null
}
}
override fun invoke(json: JsonObject): IntSpeciesFeature? {
return if (json.has(keys.first())) {
IntSpeciesFeature(keys.first(), json.get(keys.first()).asInt)
} else {
null
}
}
override fun get(pokemon: Pokemon) = pokemon.features.filterIsInstance<IntSpeciesFeature>().find { it.name in keys }
override fun encode(buffer: PacketByteBuf) {
buffer.writeCollection(keys) { _, value -> buffer.writeString(value) }
buffer.writeNullable(default) { _, value -> buffer.writeInt(value) }
buffer.writeInt(min)
buffer.writeInt(max)
buffer.writeNullable(display) { _, value -> value.encode(buffer) }
}
override fun decode(buffer: PacketByteBuf) {
keys = buffer.readList { buffer.readString() }
default = buffer.readNullable { buffer.readInt() }
min = buffer.readInt()
max = buffer.readInt()
display = buffer.readNullable { DisplayData().also { it.decode(buffer) } }
}
override fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<IntSpeciesFeature>? {
return display?.let {
BarSummarySpeciesFeatureRenderer(
name = keys.first(),
displayName = it.name.asTranslated(),
min = min,
max = max,
colour = it.colour,
underlay = it.underlay ?: cobblemonResource("textures/gui/summary/summary_stats_other_bar.png"),
overlay = it.overlay ?: cobblemonResource("textures/gui/summary/summary_stats_generic_overlay.png"),
pokemon = pokemon
)
}
}
}
@@ -8,10 +8,13 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.SpeciesFeatureAssignmentSyncPacket
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -38,17 +41,24 @@ object SpeciesFeatureAssignments : JsonDataRegistry<SpeciesFeatureAssignment> {
override val typeToken = TypeToken.get(SpeciesFeatureAssignment::class.java)
override val resourcePath = "species_feature_assignments"
private val assignments = mutableMapOf<String, MutableSet<String>>()
private val assignments = mutableMapOf<Identifier, MutableSet<String>>()
override fun sync(player: ServerPlayerEntity) {}
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(SpeciesFeatureAssignmentSyncPacket(assignments))
}
override fun reload(data: Map<Identifier, SpeciesFeatureAssignment>) {
data.values.forEach {
it.pokemon.forEach { pokemon ->
assignments.getOrPut(pokemon) { mutableSetOf() }.addAll(it.features)
assignments.getOrPut(pokemon.asIdentifierDefaultingNamespace()) { mutableSetOf() }.addAll(it.features)
}
}
this.observable.emit(this)
}
fun getFeatures(species: Species) = assignments[species.showdownId()] ?: emptySet()
fun loadOnClient(data: Map<Identifier, MutableSet<String>>) {
this.assignments.clear()
this.assignments.putAll(data)
}
fun getFeatures(species: Species) = assignments[species.resourceIdentifier] ?: emptySet()
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A provider of [SpeciesFeature]s. These must be registered via [SpeciesFeatures] either programmatically or
@@ -8,13 +8,17 @@
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.api.properties.CustomPokemonPropertyType
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.StandardSpeciesFeatureSyncPacket
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.SpeciesFeatureProviderAdapter
import com.cobblemon.mod.common.util.adapters.Vec3dAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
@@ -22,6 +26,7 @@ import com.google.gson.reflect.TypeToken
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* A registry for [SpeciesFeatureProvider]s. This is the backbone of species-specific data such as
@@ -48,11 +53,15 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
override val gson: Gson = GsonBuilder()
.setPrettyPrinting()
.registerTypeAdapter(SpeciesFeatureProvider::class.java, SpeciesFeatureProviderAdapter)
.registerTypeAdapter(Vec3d::class.java, Vec3dAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.create()
override val typeToken: TypeToken<SpeciesFeatureProvider<*>> = TypeToken.get(SpeciesFeatureProvider::class.java)
override val resourcePath: String = "species_features"
override fun sync(player: ServerPlayerEntity) {}
override fun sync(player: ServerPlayerEntity) {
player.sendPacket(StandardSpeciesFeatureSyncPacket(codeFeatures + resourceFeatures))
}
override fun reload(data: Map<Identifier, SpeciesFeatureProvider<*>>) {
resourceFeatures.keys.toList().forEach(this::unregister)
@@ -62,6 +71,9 @@ object SpeciesFeatures : JsonDataRegistry<SpeciesFeatureProvider<*>> {
fun getCodeFeature(name: String) = resourceFeatures[name]
fun getResourceFeature(name: String) = codeFeatures[name]
fun getFeature(name: String) = getCodeFeature(name) ?: getResourceFeature(name)
fun loadOnClient(entries: Collection<Map.Entry<String, SpeciesFeatureProvider<*>>>) {
codeFeatures.putAll(entries.map { it.toPair() })
}
fun getFeatures() = (resourceFeatures.keys + codeFeatures.keys).mapNotNull(this::getFeature)
fun getFeaturesFor(species: Species): List<SpeciesFeatureProvider<*>> {
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
import com.cobblemon.mod.common.pokemon.Pokemon
import com.google.gson.JsonObject
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.PacketByteBuf
/**
* A species feature value that is a string value.
@@ -22,7 +23,7 @@ import net.minecraft.nbt.NbtCompound
class StringSpeciesFeature(
override val name: String,
var value: String
) : SpeciesFeature, CustomPokemonProperty {
) : SynchronizedSpeciesFeature, CustomPokemonProperty {
override fun saveToNBT(pokemonNBT: NbtCompound): NbtCompound {
pokemonNBT.putString(name, value)
return pokemonNBT
@@ -43,6 +44,14 @@ class StringSpeciesFeature(
return this
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(value)
}
override fun decode(buffer: PacketByteBuf) {
value = buffer.readString()
}
override fun asString() = "$name=$value"
override fun apply(pokemon: Pokemon) {
@@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
/**
* A species feature that can be synchronized to the client. These must be managed by a [SynchronizedSpeciesFeatureProvider].
*
* @author Hiroku
* @since November 13th, 2023
*/
interface SynchronizedSpeciesFeature : SpeciesFeature, Encodable, Decodable
@@ -0,0 +1,33 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.pokemon.feature
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.client.gui.summary.featurerenderers.SummarySpeciesFeatureRenderer
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.network.PacketByteBuf
/**
* A species feature provider that will be synchronized to the client. For it to be renderable in the summary
* screen of a Pokémon it must also provide a [SummarySpeciesFeatureRenderer] in [getRenderer]. Note that you
* really don't want to initialize a renderer outside of that function call because if the server ever initializes
* it then you're going to get crashes on dedicated servers (this is a bad thing).
*
* @author Hiroku
* @since November 13th, 2023
*/
interface SynchronizedSpeciesFeatureProvider<T : SynchronizedSpeciesFeature> : SpeciesFeatureProvider<T>, Encodable, Decodable {
var visible: Boolean
operator fun invoke(buffer: PacketByteBuf, name: String): T?
/** Gets the feature from this Pokémon, if it has been created yet. */
fun get(pokemon: Pokemon): T?
/** Only run this from the client. */
fun getRenderer(pokemon: Pokemon): SummarySpeciesFeatureRenderer<T>?
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.pokemon.moves
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.ClientDataSynchronizer
import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.Moves
@@ -93,6 +94,20 @@ open class Learnset : ClientDataSynchronizer<Learnset> {
.flatMap { it.value }
.toSet()
fun tmLearnableMoves(): MutableList<MoveTemplate> {
val moves = mutableListOf<MoveTemplate>()
moves.addAll(tmMoves)
moves.addAll(tutorMoves)
moves.addAll(eggMoves)
moves.addAll(getLevelUpMovesUpTo(Cobblemon.config.maxPokemonLevel))
moves.addAll(evolutionMoves)
moves.addAll(formChangeMoves)
return moves
}
// We only sync level up moves atm
override fun shouldSynchronize(other: Learnset) = other.levelUpMoves != this.levelUpMoves
@@ -20,7 +20,6 @@ import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* Effect details for the actual particles of a particle effect.
@@ -29,7 +28,7 @@ import net.minecraft.util.math.Vec3d
* @since January 4th, 2023
*/
class BedrockParticle(
var texture: Identifier = Identifier("minecraft:textures/particle/bubble.png"),
var texture: Identifier = Identifier("minecraft:textures/particles/bubble.png"),
var material: ParticleMaterial = ParticleMaterial.ALPHA,
var uvMode: ParticleUVMode = StaticParticleUVMode(),
var sizeX: Expression = NumberExpression(0.15),
@@ -44,48 +43,106 @@ class BedrockParticle(
var cameraMode: ParticleCameraMode = RotateXYZCameraMode(),
var tinting: ParticleTinting = ExpressionParticleTinting(),
var collision: ParticleCollision = ParticleCollision(),
var environmentLighting: Boolean = false
var environmentLighting: Boolean = false,
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var timeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf())
) {
class ExpressionSet(
val sizeX: Expression,
val sizeY: Expression,
val maxAge: Expression,
val killExpression: Expression,
val updateExpressions: MutableList<Expression>,
val renderExpressions: MutableList<Expression>
)
/*
"creation_event": "event_sendspark",
"expiration_event": ["event_spark", "event_afterspark", "event_capturestar"],
"timeline": {
"21.00": "event_spark"
}
*/
class EventSet(
val creationEvents: MutableList<SimpleEventTrigger>,
val expirationEvents: MutableList<SimpleEventTrigger>,
val timeline: EventTriggerTimeline
)
// kill plane maybe
companion object {
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
val EXPRESSION_SET_CODEC = RecordCodecBuilder.create<ExpressionSet> { instance ->
instance.group(
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
EXPRESSION_CODEC.fieldOf("sizeX").forGetter { it.sizeX },
EXPRESSION_CODEC.fieldOf("sizeY").forGetter { it.sizeY },
EXPRESSION_CODEC.fieldOf("maxAge").forGetter { it.maxAge },
EXPRESSION_CODEC.fieldOf("killExpression").forGetter { it.killExpression },
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions },
ListCodec(EXPRESSION_CODEC).fieldOf("renderExpressions").forGetter { it.renderExpressions }
).apply(instance, ::ExpressionSet)
}
val EVENT_SET_CODEC = RecordCodecBuilder.create<EventSet> { instance ->
instance.group(
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
EventTriggerTimeline.CODEC.fieldOf("timeline").forGetter { it.timeline }
).apply(instance, ::EventSet)
}
val CODEC: Codec<BedrockParticle> = RecordCodecBuilder.create { instance ->
instance.group(
Identifier.CODEC.fieldOf("texture").forGetter { it.texture },
PrimitiveCodec.STRING.fieldOf("material").forGetter { it.material.name },
ParticleUVMode.codec.fieldOf("uvMode").forGetter { it.uvMode },
EXPRESSION_SET_CODEC.fieldOf("expressionSet").forGetter {
ExpressionSet(
it.sizeX,
it.sizeY,
it.maxAge,
it.killExpression,
it.updateExpressions,
it.renderExpressions
)
},
ParticleMotion.codec.fieldOf("motion").forGetter { it.motion },
ParticleRotation.codec.fieldOf("rotation").forGetter { it.rotation },
ParticleViewDirection.codec.fieldOf("viewDirection").forGetter { it.viewDirection },
ParticleCameraMode.codec.fieldOf("cameraMode").forGetter { it.cameraMode },
ParticleTinting.codec.fieldOf("tinting").forGetter { it.tinting },
ParticleCollision.CODEC.fieldOf("collision").forGetter { it.collision },
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting }
PrimitiveCodec.BOOL.fieldOf("environmentLighting").forGetter { it.environmentLighting },
EVENT_SET_CODEC.fieldOf("eventSet").forGetter {
EventSet(
it.creationEvents,
it.expirationEvents,
it.timeline
)
}
).apply(instance) {
texture, materialStr, uvMode, sizeX, sizeY, maxAge, killExpression, updateExpressions,
renderExpressions, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting ->
texture, materialStr, uvMode, expressionSet, motion, rotation, viewDirection, cameraMode, tinting, collision, environmentLighting, eventSet ->
BedrockParticle(
texture = texture,
material = ParticleMaterial.valueOf(materialStr),
uvMode = uvMode,
sizeX = sizeX,
sizeY = sizeY,
maxAge = maxAge,
killExpression = killExpression,
updateExpressions = updateExpressions,
renderExpressions = renderExpressions,
sizeX = expressionSet.sizeX,
sizeY = expressionSet.sizeY,
maxAge = expressionSet.maxAge,
killExpression = expressionSet.killExpression,
updateExpressions = expressionSet.updateExpressions,
renderExpressions = expressionSet.renderExpressions,
motion = motion,
rotation = rotation,
viewDirection = viewDirection,
cameraMode = cameraMode,
tinting = tinting,
collision = collision,
environmentLighting = environmentLighting
environmentLighting = environmentLighting,
creationEvents = eventSet.creationEvents,
expirationEvents = eventSet.expirationEvents,
timeline = eventSet.timeline
)
}
}
@@ -108,6 +165,9 @@ class BedrockParticle(
ParticleTinting.writeToBuffer(buffer, tinting)
collision.writeToBuffer(buffer)
buffer.writeBoolean(environmentLighting)
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
timeline.encode(buffer)
}
fun readFromBuffer(buffer: PacketByteBuf) {
@@ -127,5 +187,8 @@ class BedrockParticle(
tinting = ParticleTinting.readFromBuffer(buffer)
collision.readFromBuffer(buffer)
environmentLighting = buffer.readBoolean()
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
timeline.decode(buffer)
}
}
@@ -10,7 +10,9 @@ package com.cobblemon.mod.common.api.snowstorm
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.ListCodec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.mojang.serialization.codecs.UnboundedMapCodec
import net.minecraft.network.PacketByteBuf
import net.minecraft.util.Identifier
@@ -27,7 +29,8 @@ class BedrockParticleEffect(
var emitter: BedrockParticleEmitter = BedrockParticleEmitter(),
var particle: BedrockParticle = BedrockParticle(),
var curves: MutableList<MoLangCurve> = mutableListOf(),
var space: ParticleSpace = ParticleSpace()
var space: ParticleSpace = ParticleSpace(),
var events: MutableMap<String, ParticleEvent> = mutableMapOf()
) {
companion object {
val CODEC: Codec<BedrockParticleEffect> = RecordCodecBuilder.create { instance ->
@@ -36,14 +39,16 @@ class BedrockParticleEffect(
BedrockParticleEmitter.CODEC.fieldOf("emitter").forGetter { it.emitter },
BedrockParticle.CODEC.fieldOf("particle").forGetter { it.particle },
ListCodec(MoLangCurve.codec).fieldOf("curves").forGetter { it.curves },
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space }
).apply(instance) { id, emitter, particle, curves, space ->
ParticleSpace.CODEC.fieldOf("space").forGetter { it.space },
UnboundedMapCodec(PrimitiveCodec.STRING, ParticleEvent.CODEC).fieldOf("events").forGetter { it.events }
).apply(instance) { id, emitter, particle, curves, space, events ->
BedrockParticleEffect(
id = id,
emitter = emitter,
particle = particle,
curves = curves.toMutableList(),
space = space
space = space,
events = events
)
}
}
@@ -55,6 +60,7 @@ class BedrockParticleEffect(
particle.writeToBuffer(buffer)
buffer.writeCollection(curves) { pb, curve -> MoLangCurve.writeToBuffer(buffer, curve) }
space.writeToBuffer(buffer)
buffer.writeMap(events, { _, v -> buffer.writeString(v) }) { _, event -> event.encode(buffer) }
}
fun readFromBuffer(buffer: PacketByteBuf) {
@@ -63,5 +69,6 @@ class BedrockParticleEffect(
particle.readFromBuffer(buffer)
curves = buffer.readList { MoLangCurve.readFromBuffer(buffer) }
space.readFromBuffer(buffer)
events = buffer.readMap({ buffer.readString() }) { ParticleEvent().also { it.decode(buffer) } }
}
}
@@ -29,7 +29,12 @@ class BedrockParticleEmitter(
var updateExpressions: MutableList<Expression> = mutableListOf(),
var rate: ParticleEmitterRate = InstantParticleEmitterRate(),
var shape: ParticleEmitterShape = SphereParticleEmitterShape(),
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0))
var lifetime: ParticleEmitterLifetime = OnceEmitterLifetime(NumberExpression(1.0)),
var eventTimeline: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
var creationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var expirationEvents: MutableList<SimpleEventTrigger> = mutableListOf(),
var travelDistanceEvents: EventTriggerTimeline = EventTriggerTimeline(mutableMapOf()),
var loopingTravelDistanceEvents: MutableList<LoopingTravelDistanceEventTrigger> = mutableListOf()
) {
companion object {
val CODEC: Codec<BedrockParticleEmitter> = RecordCodecBuilder.create { instance ->
@@ -38,14 +43,24 @@ class BedrockParticleEmitter(
ListCodec(EXPRESSION_CODEC).fieldOf("updateExpressions").forGetter { it.updateExpressions },
ParticleEmitterRate.codec.fieldOf("rate").forGetter { it.rate },
ParticleEmitterShape.codec.fieldOf("shape").forGetter { it.shape },
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime }
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime ->
ParticleEmitterLifetime.codec.fieldOf("lifetime").forGetter { it.lifetime },
EventTriggerTimeline.CODEC.fieldOf("eventTimeline").forGetter { it.eventTimeline },
ListCodec(SimpleEventTrigger.CODEC).fieldOf("creationEvents").forGetter { it.creationEvents },
ListCodec(SimpleEventTrigger.CODEC).fieldOf("expirationEvents").forGetter { it.expirationEvents },
EventTriggerTimeline.CODEC.fieldOf("travelDistanceEvents").forGetter { it.travelDistanceEvents },
ListCodec(LoopingTravelDistanceEventTrigger.CODEC).fieldOf("loopingTravelDistanceEvents").forGetter { it.loopingTravelDistanceEvents }
).apply(instance) { startExpressions, updateExpressions, rate, shape, lifetime, eventTimeline, creationEvents, expirationEvents, travelDistanceEvents, loopingTravelDistanceEvents ->
BedrockParticleEmitter(
startExpressions = startExpressions,
updateExpressions = updateExpressions,
shape = shape,
rate = rate,
lifetime = lifetime
lifetime = lifetime,
eventTimeline = eventTimeline,
creationEvents = creationEvents,
expirationEvents = expirationEvents,
travelDistanceEvents = travelDistanceEvents,
loopingTravelDistanceEvents = loopingTravelDistanceEvents
)
}
}
@@ -57,6 +72,11 @@ class BedrockParticleEmitter(
ParticleEmitterRate.writeToBuffer(buffer, rate)
ParticleEmitterShape.writeToBuffer(buffer, shape)
ParticleEmitterLifetime.writeToBuffer(buffer, lifetime)
eventTimeline.encode(buffer)
buffer.writeCollection(creationEvents) { pb, event -> event.encode(pb) }
buffer.writeCollection(expirationEvents) { pb, event -> event.encode(pb) }
travelDistanceEvents.encode(buffer)
buffer.writeCollection(loopingTravelDistanceEvents) { pb, event -> event.encode(pb) }
}
fun readFromBuffer(buffer: PacketByteBuf) {
@@ -65,5 +85,10 @@ class BedrockParticleEmitter(
rate = ParticleEmitterRate.readFromBuffer(buffer)
shape = ParticleEmitterShape.readFromBuffer(buffer)
lifetime = ParticleEmitterLifetime.readFromBuffer(buffer)
eventTimeline.decode(buffer)
creationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
expirationEvents = buffer.readList { SimpleEventTrigger("").also { it.decode(buffer) } }
travelDistanceEvents.decode(buffer)
loopingTravelDistanceEvents = buffer.readList { LoopingTravelDistanceEventTrigger(0.0, mutableListOf()).also { it.decode(buffer) } }
}
}
@@ -136,6 +136,11 @@ class LoopingEmitterLifetime(var activeTime: Expression = 1.0.asExpression(), va
val interval = activeTimeValue + sleepTime
val displacement = emitterAge % interval
runtime.environment.setSimpleVariable("emitter_lifetime", activeTime)
if (emitterAge > activeTimeValue && sleepTime == 0.0) {
return ParticleEmitterAction.STOP
}
return if (displacement < activeTimeValue) {
ParticleEmitterAction.GO
} else {
@@ -145,7 +145,7 @@ class PointParticleEmitterShape(
override val type = ParticleEmitterShapeType.POINT
override fun getNewParticlePosition(runtime: MoLangRuntime, entity: Entity?) = runtime.resolveVec3d(offset)
override fun getCenter(runtime: MoLangRuntime, entity: Entity?): Vec3d {
return getNewParticlePosition(runtime, entity) // Questionable
return Vec3d.ZERO
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -0,0 +1,174 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.snowstorm
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.getQueryStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.MatrixWrapper
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.cobblemon.mod.common.util.asExpressionLike
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import net.minecraft.network.PacketByteBuf
import net.minecraft.sound.SoundCategory
import net.minecraft.sound.SoundEvent
import net.minecraft.util.Identifier
import net.minecraft.util.math.Vec3d
/**
* An event that can be referenced from various particle event triggers. The events are not necessarily particles,
* but can be combinations of particles, sounds, and MoLang expressions.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class ParticleEvent(
var particleEffect: EventParticleEffect? = null,
var soundEffect: EventSoundEffect? = null,
var expression: ExpressionLike? = null
): Encodable, Decodable {
companion object {
val CODEC: Codec<ParticleEvent> = RecordCodecBuilder.create { instance ->
instance.group(
EventParticleEffect.CODEC.optionalFieldOf("particle_effect", null).forGetter { it.particleEffect },
EventSoundEffect.CODEC.optionalFieldOf("sound_effect", null).forGetter { it.soundEffect },
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
).apply(instance) { particleEffect, soundEffect, expression ->
ParticleEvent(
particleEffect,
soundEffect,
expression?.asExpressionLike()
)
}
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeNullable(particleEffect) { pb, effect ->
pb.writeIdentifier(effect.effect)
pb.writeEnumConstant(effect.type)
pb.writeNullable(effect.expression) { pb, expr -> pb.writeString(expr.toString()) }
}
buffer.writeNullable(soundEffect) { pb, effect -> pb.writeIdentifier(effect.sound) }
buffer.writeNullable(expression) { pb, expr -> pb.writeString(expr.toString()) }
}
override fun decode(buffer: PacketByteBuf) {
particleEffect = buffer.readNullable { pb -> EventParticleEffect(
pb.readIdentifier(),
pb.readEnumConstant(EventParticleEffect.EventParticleType::class.java),
pb.readNullable { pb.readString().asExpressionLike() }
) }
soundEffect = buffer.readNullable { pb -> EventSoundEffect(pb.readIdentifier()) }
expression = buffer.readNullable { pb -> pb.readString().asExpressionLike() }
}
fun run(storm: ParticleStorm, particle: SnowstormParticle?) {
particleEffect?.let { effect ->
val bedrockParticleEffect = BedrockParticleEffectRepository.getEffect(effect.effect) ?: return@let
val rootMatrix = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER -> MatrixWrapper().updatePosition(storm.matrixWrapper.getOrigin())
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.matrixWrapper
EventParticleEffect.EventParticleType.PARTICLE,
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())).let { MatrixWrapper().updatePosition(it) }
}
val sourceVelocity = when (effect.type) {
EventParticleEffect.EventParticleType.EMITTER -> storm.sourceVelocity().let { { it } }
EventParticleEffect.EventParticleType.EMITTER_BOUND -> storm.sourceVelocity
EventParticleEffect.EventParticleType.PARTICLE -> { { Vec3d.ZERO } }
EventParticleEffect.EventParticleType.PARTICLE_WITH_VELOCITY -> (particle?.let { Vec3d(it.getVelocityX(), it.getVelocityY(), it.getVelocityZ()) } ?: Vec3d.ZERO).let { { it } }
}
val newStorm = ParticleStorm(
effect = bedrockParticleEffect,
matrixWrapper = rootMatrix,
world = storm.world,
sourceVelocity = sourceVelocity,
sourceAlive = storm.sourceAlive,
sourceVisible = storm.sourceVisible,
onDespawn = {},
runtime = MoLangRuntime().setup().also { it.environment.structs["query"] = storm.runtime.environment.getQueryStruct() },
entity = storm.entity
)
effect.expression?.resolve(newStorm.runtime)
newStorm.spawn()
}
soundEffect?.let { effect ->
val position = particle?.let { Vec3d(it.getX(), it.getY(), it.getZ()) } ?: Vec3d(storm.getX(), storm.getY(), storm.getZ())
val world = storm.world
val soundEvent = SoundEvent.of(effect.sound)
world.playSound(position.x, position.y, position.z, soundEvent, SoundCategory.NEUTRAL, 1F, 1F, true)
}
expression?.resolve(storm.runtime)
}
}
/**
* A particle component of a [ParticleEvent]. It contains the effect to play, the spawning type, and a pre-run
* expression to run in the new storm's runtime.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class EventParticleEffect(
val effect: Identifier,
val type: EventParticleType,
val expression: ExpressionLike? = null
) {
companion object {
val CODEC = RecordCodecBuilder.create<EventParticleEffect> { instance ->
instance.group(
Identifier.CODEC.fieldOf("effect").forGetter { it.effect },
PrimitiveCodec.STRING.fieldOf("type").forGetter { it.type.name },
PrimitiveCodec.STRING.optionalFieldOf("expression", null).forGetter { it.expression?.toString() }
).apply(instance) { effect, type, expression ->
EventParticleEffect(
effect,
EventParticleType.valueOf(type),
expression?.asExpressionLike()
)
}
}
}
enum class EventParticleType {
EMITTER,
EMITTER_BOUND,
PARTICLE,
PARTICLE_WITH_VELOCITY
}
}
/**
* A simple sound effect component of a [ParticleEvent]. Plays a sound at the particle location, or if run from an
* emitter event, at the emitter's location.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class EventSoundEffect(
val sound: Identifier,
) {
companion object {
val CODEC = RecordCodecBuilder.create<EventSoundEffect> { instance ->
instance.group(
Identifier.CODEC.fieldOf("sound").forGetter { it.sound }
).apply(instance, ::EventSoundEffect)
}
}
}
@@ -0,0 +1,118 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.snowstorm
import com.cobblemon.mod.common.api.net.Decodable
import com.cobblemon.mod.common.api.net.Encodable
import com.cobblemon.mod.common.client.particle.ParticleStorm
import com.cobblemon.mod.common.client.render.SnowstormParticle
import com.mojang.serialization.codecs.PrimitiveCodec
import com.mojang.serialization.codecs.RecordCodecBuilder
import com.mojang.serialization.codecs.UnboundedMapCodec
import net.minecraft.network.PacketByteBuf
/*
* The different references to events that can be triggered by an effect.
*/
/**
* It really is simple.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class SimpleEventTrigger(var event: String): Encodable, Decodable {
companion object {
val CODEC = RecordCodecBuilder.create<SimpleEventTrigger> { instance ->
instance.group(
PrimitiveCodec.STRING.fieldOf("event").forGetter { it.event }
).apply(instance, ::SimpleEventTrigger)
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeString(event)
}
override fun decode(buffer: PacketByteBuf) {
event = buffer.readString()
}
fun trigger(storm: ParticleStorm, particle: SnowstormParticle?) {
val event = storm.effect.events[event] ?: return
event.run(storm, particle)
}
}
/**
* An event trigger that outlines specific times at which point groups of effects will play. This can be used
* on the basis of time but is also used in the case of travel distance.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class EventTriggerTimeline(var map: MutableMap<Double, MutableList<String>>): Encodable, Decodable {
companion object {
val CODEC = RecordCodecBuilder.create<EventTriggerTimeline> { instance ->
instance.group(
UnboundedMapCodec(PrimitiveCodec.DOUBLE, PrimitiveCodec.STRING.listOf()).fieldOf("map").forGetter { it.map }
).apply(instance, ::EventTriggerTimeline)
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeMap(map, { pb, k -> pb.writeDouble(k) }, { pb, v -> pb.writeCollection(v) { _, s -> pb.writeString(s) } })
}
override fun decode(buffer: PacketByteBuf) {
map = buffer.readMap({ pb -> pb.readDouble() }, { pb -> pb.readList { pb.readString() } })
}
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousTime: Double, newTime: Double) {
val events = map.entries.filter { it.key in previousTime..newTime }.flatMap { it.value }
events.forEach { event ->
val event = storm.effect.events[event] ?: return
event.run(storm, particle)
}
}
}
/**
* An event trigger that takes the total distance travelled by the emitter and modulos it across [distance] to know
* if we need to play it again.
*
* @author Hiroku
* @since March 2nd, 2024
*/
class LoopingTravelDistanceEventTrigger(var distance: Double, var events: MutableList<String>): Encodable, Decodable {
companion object {
val CODEC = RecordCodecBuilder.create<LoopingTravelDistanceEventTrigger> { instance ->
instance.group(
PrimitiveCodec.DOUBLE.fieldOf("distance").forGetter { it.distance },
PrimitiveCodec.STRING.listOf().fieldOf("events").forGetter { it.events }
).apply(instance, ::LoopingTravelDistanceEventTrigger)
}
}
override fun encode(buffer: PacketByteBuf) {
buffer.writeDouble(distance)
buffer.writeCollection(events) { _, s -> buffer.writeString(s) }
}
override fun decode(buffer: PacketByteBuf) {
distance = buffer.readDouble()
events = buffer.readList { buffer.readString() }
}
fun check(storm: ParticleStorm, particle: SnowstormParticle?, previousDistance: Double, currentDistance: Double) {
if (previousDistance < distance && currentDistance >= distance) {
events.mapNotNull { storm.effect.events[it] }.forEach { it.run(storm, particle) }
}
}
}
@@ -140,7 +140,7 @@ class InwardsMotionDirection : ParticleMotionDirection {
return if (particlePos == emitterPos) {
Vec3d(storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5, storm.world.random.nextDouble() - 0.5)
} else {
particlePos.subtract(emitterPos)
emitterPos.subtract(particlePos)
}.normalize()
}
override fun <T> encode(ops: DynamicOps<T>) = CODEC.encodeStart(ops, this)
@@ -30,9 +30,16 @@ abstract class SingleEntitySpawnAction<T : Entity>(
override fun run(): EntitySpawnResult? {
val e = createEntity() ?: return null
e.setPosition(ctx.position.toVec3d().add(0.5, 1.0, 0.5))
entity.emit(e)
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = { ctx.world.spawnEntity(e) })
return EntitySpawnResult(listOf(e))
var shouldSpawn = false
CobblemonEvents.ENTITY_SPAWN.postThen(SpawnEvent(e, ctx), ifSucceeded = {
ctx.world.spawnEntity(e)
shouldSpawn = true
})
return if (shouldSpawn) {
this.entity.emit(e)
EntitySpawnResult(listOf(e))
} else null
}
@@ -0,0 +1,51 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.spawning.influence
import com.cobblemon.mod.common.api.spawning.context.SpawningContext
import com.cobblemon.mod.common.api.spawning.detail.SpawnDetail
import net.minecraft.registry.tag.BlockTags
/**
* Implements a [SpawningInfluence] that prevents Pokémon spawning on certain blocks.
* This ensures that Pokémon don't spawn in areas that could disrupt player-built structures or mechanisms.
* The influence checks the block directly above the potential spawn location; if the block is in the restricted list,
* the spawn is disallowed.
*
* @author Aethen
* @since February 20th, 2024
*/
open class RestrictedSpawnBlocksInfluence
: SpawningInfluence {
// The list of blocks that Pokémon are not allowed to spawn on.
private val restrictedBlocks = BlockTags.RAILS
/**
* Affects the spawnable status of a Pokémon spawn detail.
* If the block directly above the spawn location is in the restricted list, the spawn is disallowed.
*
* @param detail The Pokémon spawn detail to be affected.
* @param ctx The context of the spawning event.
* @return `true` if the spawn is allowed, `false` if it is disallowed.
*/
override fun affectSpawnable(detail: SpawnDetail, ctx: SpawningContext): Boolean {
// If the spawn detail is not a Pokémon, it is not affected by this influence.
if (detail.type != "pokemon") return true
// Get the position of the potential spawn location.
val pokemonSpawnPos = ctx.position
// Examine the block immediately above the spawn block to check for a rail
// Potentially need a refactor if we ever want to check the spawn block itself.
return ctx.world.getBlockState(pokemonSpawnPos.withY(pokemonSpawnPos.y + 1)).isIn(restrictedBlocks).not()
}
}
@@ -13,8 +13,10 @@ import com.cobblemon.mod.common.api.spawning.CobblemonSpawnRules
import com.cobblemon.mod.common.api.spawning.SpawnerManager
import com.cobblemon.mod.common.api.spawning.detail.SpawnPool
import com.cobblemon.mod.common.api.spawning.influence.PlayerLevelRangeInfluence
import com.cobblemon.mod.common.api.spawning.influence.RestrictedSpawnBlocksInfluence
import com.cobblemon.mod.common.api.spawning.influence.SpawningInfluence
import com.cobblemon.mod.common.api.spawning.rules.SpawnRule
import com.cobblemon.mod.common.util.mutableLazy
import net.minecraft.server.network.ServerPlayerEntity
/**
@@ -26,14 +28,31 @@ import net.minecraft.server.network.ServerPlayerEntity
* @since February 14th, 2022
*/
object PlayerSpawnerFactory {
var spawns: SpawnPool = CobblemonSpawnPools.WORLD_SPAWN_POOL
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({ PlayerLevelRangeInfluence(it, variation = 5) })
var spawns: SpawnPool by mutableLazy {
CobblemonSpawnPools.WORLD_SPAWN_POOL
}
/**
* The list of influences for a new [PlayerSpawner].
* The influences will be added to the spawner in the order they are added to this list.
*
* [PlayerLevelRangeInfluence] adjusts the level range of Pokémon that can spawn based on the player's level.
* [RestrictedSpawnBlocksInfluence] prevents Pokémon from spawning on certain blocks.
*/
var influenceBuilders = mutableListOf<(player: ServerPlayerEntity) -> SpawningInfluence?>({
PlayerLevelRangeInfluence(
it,
variation = 5
)
}, { RestrictedSpawnBlocksInfluence() })
fun create(spawnerManager: SpawnerManager, player: ServerPlayerEntity): PlayerSpawner {
val influences = influenceBuilders.mapNotNull { it(player) }
return PlayerSpawner(player, spawns, spawnerManager).also {
it.influences.addAll(influences)
it.influences.addAll(CobblemonSpawnRules.rules.values.filter(SpawnRule::enabled).flatMap(SpawnRule::components))
it.influences.addAll(
CobblemonSpawnRules.rules.values.filter(SpawnRule::enabled).flatMap(SpawnRule::components)
)
}
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.storage.adapter.conversions
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokemon.Natures
@@ -98,8 +99,9 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<NbtCompound> {
result.level = nbt.getInt("Level")
result.addExperience(SidemodExperienceSource("Reforged"), nbt.getInt("EXP"))
result.setFriendship(nbt.getInt("Friendship"))
result.ability = (result.form.abilities.find { it.template.name == nbt.getString("Ability") } ?: result.form.abilities.first())
.template.create()
Abilities.get(nbt.getString("Ability"))?.let { template ->
result.updateAbility(template.create(forced = result.form.abilities.none { it.template == template }))
}
result.nature = Natures.getNature(Identifier(ReforgedNatures.values()[nbt.getInt("Nature")].name.lowercase())) ?: Natures.getRandomNature()
result.mintedNature = Natures.getNature(Identifier(ReforgedNatures.values()[nbt.getInt("MintNature")].name.lowercase()))
result.currentHealth = nbt.getInt("Health")
@@ -11,12 +11,14 @@ package com.cobblemon.mod.common.api.storage.party
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PartyCheckContext
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.PassiveEvolution
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.pokemon.OriginalTrainerType
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.util.*
import net.minecraft.nbt.NbtCompound
import net.minecraft.server.network.ServerPlayerEntity
@@ -123,6 +125,12 @@ open class PlayerPartyStore(
// Passive evolutions
pokemon.evolutions.filterIsInstance<PassiveEvolution>().forEach { it.attemptEvolution(pokemon) }
val removeList = mutableListOf<Evolution>()
pokemon.evolutionProxy.server().forEach {
if (!it.test(pokemon) && it is LevelUpEvolution && !it.permanent)
removeList.add(it)
}
removeList.forEach { pokemon.evolutionProxy.server().remove(it) }
}
// Friendship
// ToDo expand this down the line just a very basic implementation for the first releases
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.storage.player
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.net.messages.client.starter.SetClientPlayerDataPacket
import java.util.UUID
@@ -23,6 +24,7 @@ data class PlayerData(
var starterSelected: Boolean,
var starterUUID: UUID?,
var keyItems: MutableSet<Identifier>,
var tmSet: MutableSet<Identifier>,
var battleTheme: Identifier?,
val extraData: MutableMap<String, PlayerDataExtension>,
) {
@@ -42,6 +44,7 @@ data class PlayerData(
starterUUID = null,
keyItems = mutableSetOf(),
battleTheme = CobblemonSounds.PVP_BATTLE.id,
tmSet = mutableSetOf(),
extraData = mutableMapOf()
)
}
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.api.storage.player.adapter
import com.cobblemon.mod.common.api.storage.player.PlayerData
import com.cobblemon.mod.common.api.storage.player.PlayerDataExtension
import com.cobblemon.mod.common.api.tms.ObtainMethod
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.fromJson
import com.google.gson.GsonBuilder
@@ -31,6 +33,7 @@ class JsonPlayerData: PlayerDataStoreAdapter {
.setPrettyPrinting()
.disableHtmlEscaping()
.registerTypeAdapter(PlayerDataExtension::class.java, PlayerDataExtensionAdapter)
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.create()
}
@@ -144,6 +144,9 @@ object CobblemonItemTags {
@JvmField
val POTTERY_SHERDS = create("decorated_pot_sherds")
@JvmField
val ABILITY_CHANGERS = create("ability_changers")
private fun create(path: String) = TagKey.of(RegistryKeys.ITEM, cobblemonResource(path))
}
@@ -0,0 +1,26 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.tms
import net.minecraft.server.network.ServerPlayerEntity
interface ObtainMethod {
/**
* Whether to check this [ObtainMethod] once every second.
*/
val passive: Boolean
/**
* Checks if the [ObtainMethod] condition is true.
*
* @param player The player to use when checking.
* @return Whether the check succeeded.
*/
fun matches(player: ServerPlayerEntity): Boolean
}
@@ -0,0 +1,23 @@
package com.cobblemon.mod.common.api.tms
import com.google.gson.JsonDeserializer
import com.google.gson.JsonSerializer
import net.minecraft.util.Identifier
import kotlin.reflect.KClass
/**
* A type adapter for [ObtainMethod]s.
* For the default implementation see [CobblemonObtainMethodAdapter].
*
* @author Apion
* @since November 22nd, 2023
*/
interface ObtainMethodAdapter : JsonDeserializer<ObtainMethod>, JsonSerializer<ObtainMethod> {
/**
* Register a [ObtainMethod] to be used by this adapter.
*
* @param type The [KClass] of the [ObtainMethod].
* @param identifier The expected [Identifier] in the parsed JSON.
*/
fun register(type: KClass<out ObtainMethod>, identifier: Identifier)
}
@@ -0,0 +1,119 @@
/*
* 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.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.tms.obtain.NoneObtainMethod
import com.cobblemon.mod.common.util.lang
import net.minecraft.item.Item
import net.minecraft.item.Items
import net.minecraft.registry.Registries
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class TechnicalMachine(
val moveName: String,
val recipe: TechnicalMachineRecipe?,
val obtainMethods: List<ObtainMethod> = emptyList(),
val type: String,
val primaryColor: Int?,
val secondaryColor: Int?
) {
companion object {
/**
* Filters all available [TechnicalMachines] based on three optional arguments
*
* @param search A [String] to check against move names
* @param type An [ElementalType] to check for
* @param pokemon A [Pokemon] to check the [Learnset] of
* @return A [MutableSet] of [TechnicalMachine] that passed the filter
* @author whatsy
*/
fun filterTms(search: String?, type: ElementalType?, pokemon: Pokemon?): MutableSet<TechnicalMachine> {
val tms = TechnicalMachines.tmMap.values.toMutableSet()
type?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (ElementalTypes.get(tm.type) != type) {
iterator.remove()
}
}
}
pokemon?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
val move = Moves.getByName(tm.moveName)
if (!pokemon.species.moves.tmLearnableMoves().contains(move)) {
iterator.remove()
}
}
}
search?.let {
val iterator = tms.iterator()
while (iterator.hasNext()) {
val tm = iterator.next()
if (!tm.translatedMoveName().toString().contains(search, true)) {
iterator.remove()
}
}
}
return tms
}
}
/**
* Gets the [Identifier] of this [TechnicalMachine]
*
* @return The [Identifier] of this [TechnicalMachine] (or null if not found)
* @author whatsy
*/
fun id(): Identifier? {
TechnicalMachines.tmMap.forEach {
if (TechnicalMachines.tmMap[it.key] == this) return it.key
}
return null
}
/**
* Unlocks this [TechnicalMachine] for the player.
*
* @param player The [ServerPlayerEntity] to give this [TechnicalMachine] to.
* @return Whether the player was successfully granted the [TechnicalMachine]
* @author whatsy
*/
fun unlock(player: ServerPlayerEntity): Boolean {
if (id() == null) return false
Cobblemon.playerData.get(player).tmSet.add(id()!!)
if (!obtainMethods.any { it is NoneObtainMethod }) player.sendMessage(lang("tms.unlock_tm", lang("move.$moveName")))
return true
}
/**
* Returns a [MutableText] of the translated move name of this [TechnicalMachine]
*
* @return This [TechnicalMachine]'s move name, translated
* @author whatsy
*/
fun translatedMoveName(): MutableText {
return lang("move." + this.moveName)
}
}
@@ -6,15 +6,14 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.widgets
package com.cobblemon.mod.common.api.tms
import com.cobblemon.mod.common.util.cobblemonResource
class TypeDisplayWidget {
import net.minecraft.util.Identifier
companion object {
private val TYPES_RESOURCE = cobblemonResource("textures/gui/types.png")
}
}
/**
* Represents the ingredients necessary to craft a [TechnicalMachine] in the [TMBlock]
*/
data class TechnicalMachineRecipe(
val item: Identifier,
val count: Int
)
@@ -0,0 +1,76 @@
/*
* 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.tms
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.item.TechnicalMachineItem
import com.cobblemon.mod.common.registry.ItemTagCondition
import com.cobblemon.mod.common.util.adapters.CobblemonObtainMethodAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.item.ItemStack
import net.minecraft.registry.Registries
import net.minecraft.resource.ResourceType
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.util.Identifier
object TechnicalMachines : JsonDataRegistry<TechnicalMachine> {
override val gson = GsonBuilder()
.registerTypeAdapter(Identifier::class.java, IdentifierAdapter)
.registerTypeAdapter(ObtainMethod::class.java, CobblemonObtainMethodAdapter)
.create()
override val typeToken = TypeToken.get(TechnicalMachine::class.java)
override val resourcePath = "tms"
override val id = cobblemonResource("technical_machines")
override val type = ResourceType.SERVER_DATA
override val observable = SimpleObservable<TechnicalMachines>()
val tmMap = mutableMapOf<Identifier, TechnicalMachine>()
val tagMap = mutableMapOf<ItemTagCondition, TechnicalMachine>()
val passiveTms = mutableMapOf<Identifier, TechnicalMachine>()
override fun reload(data: Map<Identifier, TechnicalMachine>) {
data.forEach {id, tm ->
tmMap[id] = tm
if (tm.obtainMethods.any { it.passive }) passiveTms.put(id, tm)
}
}
override fun sync(player: ServerPlayerEntity) { }
fun getTechnicalMachineFromStack(item: ItemStack?): TechnicalMachine? {
if (item == null) {
return null
}
return TechnicalMachineItem.getMoveNbt(item)
}
fun getStackFromTechnicalMachine(tm: TechnicalMachine): ItemStack {
val tmItem = CobblemonItems.TECHNICAL_MACHINE.defaultStack
return CobblemonItems.TECHNICAL_MACHINE.setNbt(tmItem,tm.id().toString())
}
fun isTechnicalMachine(item: ItemStack): Boolean {
return item.item is TechnicalMachineItem
}
fun checkPassives(player: ServerPlayerEntity) {
val playerTms = Cobblemon.playerData.get(player).tmSet
passiveTms.forEach { (id, tm) ->
if (tm.obtainMethods.all { it.matches(player) } && !playerTms.contains(id)) tm.unlock(player)
}
}
}
@@ -13,17 +13,23 @@ import net.minecraft.text.MutableText
import net.minecraft.util.Identifier
/**
* Class representing a type of a Pokemon or Move
* Class representing a type of Pokémon or Move
*
* @param name: The English name used to load / find it (spaces -> _)
* @param displayName: A Component used to display the name, normally a TranslatableText
* @param textureXMultiplier: The multiplier by which the TypeWidget shall move the display
* @param resourceLocation: The location of the resource used in the TypeWidget
* @param primaryColor: The primary color of this type used to color [TechnicalMachineItem].
* @param secondaryColor: The secondary color of this type used to color [TechnicalMachineItem]
* @param typeGem: The Type Gem item used to craft a [TechnicalMachineItem] of this type.
*/
class ElementalType(
val name: String,
val displayName: MutableText,
val hue: Int,
val textureXMultiplier: Int,
val resourceLocation: Identifier = Identifier(Cobblemon.MODID, "ui/types.png")
val resourceLocation: Identifier = Identifier(Cobblemon.MODID, "ui/types.png"),
val primaryColor: Int,
val secondaryColor: Int,
val typeGem: Identifier
)
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.api.types
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.text.MutableText
import net.minecraft.text.Text
@@ -22,134 +23,180 @@ object ElementalTypes {
name = "normal",
displayName = Text.translatable("cobblemon.type.normal"),
hue = 0xDDDDCF,
textureXMultiplier = 0
textureXMultiplier = 0,
primaryColor = 0xCCCCC1,
secondaryColor = 0xF9FFFC
)
val FIRE = register(
name = "fire",
displayName = Text.translatable("cobblemon.type.fire"),
hue = 0xE55C32,
textureXMultiplier = 1
textureXMultiplier = 1,
primaryColor = 0xFF8766,
secondaryColor = 0xFFD9A5
)
val WATER = register(
name = "water",
displayName = Text.translatable("cobblemon.type.water"),
hue = 0x4A9BE8,
textureXMultiplier = 2
textureXMultiplier = 2,
primaryColor = 0x7F9DFF,
secondaryColor = 0xA5EEFF
)
val GRASS = register(
name = "grass",
displayName = Text.translatable("cobblemon.type.grass"),
hue = 0x4DBC3C,
textureXMultiplier = 3
textureXMultiplier = 3,
primaryColor = 0x66CC99,
secondaryColor = 0xD6FFA5
)
val ELECTRIC = register(
name = "electric",
displayName = Text.translatable("cobblemon.type.electric"),
hue = 0xEFD128,
textureXMultiplier = 4
textureXMultiplier = 4,
primaryColor = 0xFFCF3F,
secondaryColor = 0xFFFFBF
)
val ICE = register(
name = "ice",
displayName = Text.translatable("cobblemon.type.ice"),
hue = 0x6BC3EF,
textureXMultiplier = 5
textureXMultiplier = 5,
primaryColor = 0x8CC7FF,
secondaryColor = 0xD6FFFF
)
val FIGHTING = register(
name = "fighting",
displayName = Text.translatable("cobblemon.type.fighting"),
hue = 0xC44C5C,
textureXMultiplier = 6
textureXMultiplier = 6,
primaryColor = 0xCC6673,
secondaryColor = 0xFFADA5
)
val POISON = register(
name = "poison",
displayName = Text.translatable("cobblemon.type.poison"),
hue = 0xA24BD8,
textureXMultiplier = 7
textureXMultiplier = 7,
primaryColor = 0x8D66CC,
secondaryColor = 0xEDA5FF
)
val GROUND = register(
name = "ground",
displayName = Text.translatable("cobblemon.type.ground"),
hue = 0xD89950,
textureXMultiplier = 8
textureXMultiplier = 8,
primaryColor = 0xE5A27E,
secondaryColor = 0xFFE1A5
)
val FLYING = register(
name = "flying",
displayName = Text.translatable("cobblemon.type.flying"),
hue = 0xBCC1FF,
textureXMultiplier = 9
textureXMultiplier = 9,
primaryColor = 0x8CB6FF,
secondaryColor = 0xE8DDFF
)
val PSYCHIC = register(
name = "psychic",
displayName = Text.translatable("cobblemon.type.psychic"),
hue = 0xD86AD6,
textureXMultiplier = 10
textureXMultiplier = 10,
primaryColor = 0xE48CFF,
secondaryColor = 0xFFCCF6
)
val BUG = register(
name = "bug",
displayName = Text.translatable("cobblemon.type.bug"),
hue = 0xA2C831,
textureXMultiplier = 11
textureXMultiplier = 11,
primaryColor = 0xAFCC66,
secondaryColor = 0xF6FFBF
)
val ROCK = register(
name = "rock",
displayName = Text.translatable("cobblemon.type.rock"),
hue = 0xAA9666,
textureXMultiplier = 12
textureXMultiplier = 12,
primaryColor = 0x997F4C,
secondaryColor = 0xDBC6A4
)
val GHOST = register(
name = "ghost",
displayName = Text.translatable("cobblemon.type.ghost"),
hue = 0x9572E5,
textureXMultiplier = 13
textureXMultiplier = 13,
primaryColor = 0x8E8CFF,
secondaryColor = 0xE8A5FF
)
val DRAGON = register(
name = "dragon",
displayName = Text.translatable("cobblemon.type.dragon"),
hue = 0x535DE8,
textureXMultiplier = 14
textureXMultiplier = 14,
primaryColor = 0xBA8CFF,
secondaryColor = 0xA5E5FF
)
val DARK = register(
name = "dark",
displayName = Text.translatable("cobblemon.type.dark"),
hue = 0x5C6CB2,
textureXMultiplier = 15
textureXMultiplier = 15,
primaryColor = 0x5966B2,
secondaryColor = 0xA5B1FF
)
val STEEL = register(
name = "steel",
displayName = Text.translatable("cobblemon.type.steel"),
hue = 0xC3CCE0,
textureXMultiplier = 16
textureXMultiplier = 16,
primaryColor = 0x858EB2,
secondaryColor = 0xF4FFFD
)
val FAIRY = register(
name = "fairy",
displayName = Text.translatable("cobblemon.type.fairy"),
hue = 0xEA727E,
textureXMultiplier = 17
textureXMultiplier = 17,
primaryColor = 0xFF8CB6,
secondaryColor = 0xFFD0CC
)
fun register(name: String, displayName: MutableText, hue: Int, textureXMultiplier: Int): ElementalType {
fun register(
name: String,
displayName: MutableText,
hue: Int,
textureXMultiplier: Int,
primaryColor: Int,
secondaryColor: Int
): ElementalType {
return ElementalType(
name = name,
displayName = displayName,
hue = hue,
textureXMultiplier = textureXMultiplier
textureXMultiplier = textureXMultiplier,
primaryColor = primaryColor,
secondaryColor = secondaryColor,
typeGem = cobblemonResource("${name}_gem")
).also {
allTypes.add(it)
}
@@ -21,6 +21,7 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
import com.cobblemon.mod.common.battles.dispatch.WaitDispatch
import com.cobblemon.mod.common.net.messages.client.animation.PlayPoseableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
import com.cobblemon.mod.common.util.battleLang
@@ -70,18 +71,26 @@ class DamageInstruction(
}
battle.dispatch {
if (!causedFaint) {
val pokemonEntity = battlePokemon.entity
if (!causedFaint && pokemonEntity != null) {
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
pkt.sendToPlayersAround(
x = pokemonEntity.x,
y = pokemonEntity.y,
z = pokemonEntity.z,
worldKey = pokemonEntity.world.registryKey,
distance = 50.0
)
}
battlePokemon.entity?.let { pokemonEntity ->
val pkt = PlayPoseableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptySet())
pkt.sendToPlayersAround(
x = pokemonEntity.x,
y = pokemonEntity.y,
z = pokemonEntity.z,
worldKey = pokemonEntity.world.registryKey,
distance = 50.0
)
}
if (pokemonEntity != null) {
RunPosableMoLangPacket(pokemonEntity.id, setOf("q.particle('cobblemon:hit', 'target')")).sendToPlayersAround(
x = pokemonEntity.x,
y = pokemonEntity.y,
z = pokemonEntity.z,
worldKey = pokemonEntity.world.registryKey,
distance = 50.0
)
}
@@ -0,0 +1,74 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import net.minecraft.block.*
import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.WorldAccess
class CoinPouchBlock(settings: Settings, val small: Boolean) : HorizontalFacingBlock(settings) {
init {
this.defaultState = this.stateManager.defaultState
.with(NATURAL, false)
.with(FACING, Direction.NORTH)
}
override fun getOutlineShape(
state: BlockState?,
world: BlockView?,
pos: BlockPos?,
context: ShapeContext?
) = if (!small) super.getOutlineShape(state, world, pos, context) else {
VoxelShapes.cuboid(0.3125, 0.0, 0.3125, 0.6875, 0.3125, 0.6875)
}
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
var blockState = defaultState
val worldView = ctx.world
val blockPos = ctx.blockPos
ctx.placementDirections.forEach { direction ->
if (direction.axis.isHorizontal) {
blockState = blockState.with(FACING, direction.opposite) as BlockState
if (blockState.canPlaceAt(worldView, blockPos)) {
return blockState
}
}
}
return null
}
@Deprecated("Deprecated in Java")
override fun getStateForNeighborUpdate(
state: BlockState,
direction: Direction,
neighborState: BlockState,
world: WorldAccess,
pos: BlockPos,
neighborPos: BlockPos
): BlockState {
return if (direction == state.get(FACING) && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(FACING, NATURAL)
}
companion object {
val NATURAL: BooleanProperty = BooleanProperty.of("natural")
}
}
@@ -0,0 +1,111 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.block.entity.DisplayCaseBlockEntity
import com.cobblemon.mod.common.item.PokeBallItem
import net.minecraft.block.*
import net.minecraft.block.HorizontalFacingBlock.*
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.BlockItem
import net.minecraft.item.ItemPlacementContext
import net.minecraft.state.StateManager
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
@Suppress("OVERRIDE_DEPRECATION")
class DisplayCaseBlock(settings: Settings) : BlockWithEntity(settings) {
init {
this.defaultState = this.stateManager.defaultState
.with(FACING, Direction.NORTH)
}
override fun getPlacementState(ctx: ItemPlacementContext): BlockState? {
var blockState = defaultState
val worldView = ctx.world
val blockPos = ctx.blockPos
ctx.placementDirections.forEach { direction ->
if (direction.axis.isHorizontal) {
blockState = blockState.with(FACING, direction) as BlockState
if (blockState.canPlaceAt(worldView, blockPos)) {
return blockState
}
}
}
return null
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(FACING)
}
override fun createBlockEntity(pos: BlockPos, state: BlockState): DisplayCaseBlockEntity {
return DisplayCaseBlockEntity(pos, state)
}
override fun getStateForNeighborUpdate(
state: BlockState,
direction: Direction,
neighborState: BlockState,
world: WorldAccess,
pos: BlockPos,
neighborPos: BlockPos
): BlockState {
return if (direction == state.get(FACING) && !state.canPlaceAt(world, pos)) Blocks.AIR.defaultState
else super.getStateForNeighborUpdate(state, direction, neighborState, world, pos, neighborPos)
}
override fun onUse(
state: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
hand: Hand,
hit: BlockHitResult
): ActionResult {
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
val result = entity.updateItem(player, hand)
if ((hit.side != Direction.UP && hit.side != Direction.DOWN) && result == ActionResult.SUCCESS) {
world.setBlockState(pos, state.with(FACING, hit.side.opposite))
}
return result
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
val entity = world.getBlockEntity(pos) as DisplayCaseBlockEntity
if (!entity.getStack().isEmpty && !player.isCreative) {
ItemScatterer.spawn(world, pos, entity.inv)
}
super.onBreak(world, pos, state, player)
}
override fun getRenderType(state: BlockState?) = BlockRenderType.MODEL
override fun getComparatorOutput(state: BlockState, world: World, pos: BlockPos): Int {
val stack = (world.getBlockEntity(pos) as DisplayCaseBlockEntity).getStack()
if (stack.isEmpty) return 0
if (stack.item is PokeBallItem) return 3
if (stack.item is BlockItem) return 2
return 1
}
override fun hasComparatorOutput(state: BlockState?) = true
override fun canPathfindThrough(state: BlockState, world: BlockView, pos: BlockPos, type: NavigationType) = false
}
@@ -0,0 +1,128 @@
package com.cobblemon.mod.common.block
import net.minecraft.block.AbstractBlock.Settings
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.Fluids
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.screen.NamedScreenHandlerFactory
import net.minecraft.state.StateManager
import net.minecraft.state.property.EnumProperty
import net.minecraft.util.*
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
import net.minecraft.world.WorldView
class ModStationBlock(settings: Settings) : Block(settings) {
companion object {
val PART = EnumProperty.of("part", ModStationPart::class.java)
}
enum class ModStationPart(private val label: String) : StringIdentifiable {
TOP("top"),
BOTTOM("bottom");
override fun asString() = label
}
init {
defaultState = this.stateManager.defaultState.with(
HorizontalFacingBlock.FACING, Direction.SOUTH
).with(PART, ModStationPart.BOTTOM)
}
override fun onPlaced(
world: World,
pos: BlockPos,
state: BlockState,
placer: LivingEntity?,
itemStack: ItemStack
) {
world.setBlockState(pos.up(), state
.with(PART, ModStationPart.TOP)
)
world.updateNeighbors(pos, Blocks.AIR)
state.updateNeighbors(world, pos, 3)
}
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
val abovePosition = blockPlaceContext.blockPos.up()
val world = blockPlaceContext.world
if (world.getBlockState(abovePosition).canReplace(blockPlaceContext) && !world.isOutOfHeightLimit(abovePosition)) {
return defaultState
.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing)
.with(PART, ModStationPart.BOTTOM)
}
return null
}
override fun canPlaceAt(state: BlockState, world: WorldView, pos: BlockPos): Boolean {
val blockPos = pos.down()
val blockState = world.getBlockState(blockPos)
return if (state.get(PART) == ModStationPart.BOTTOM) blockState.isSideSolidFullSquare(world, blockPos, Direction.UP) else blockState.isOf(this)
}
override fun canPathfindThrough(
state: BlockState?,
world: BlockView?,
pos: BlockPos?,
type: NavigationType?
) = false
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(HorizontalFacingBlock.FACING)
builder.add(PART)
}
override fun rotate(blockState: BlockState, rotation: BlockRotation) = blockState.with(HorizontalFacingBlock.FACING, rotation.rotate(blockState.get(HorizontalFacingBlock.FACING)))
override fun mirror(blockState: BlockState, mirror: BlockMirror): BlockState {
return blockState.rotate(mirror.getRotation(blockState.get(HorizontalFacingBlock.FACING)))
}
override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos?, newState: BlockState, moved: Boolean) {
if (!state.isOf(newState.block)) super.onStateReplaced(state, world, pos, newState, moved)
}
override fun getStateForNeighborUpdate(
state: BlockState,
direction: Direction,
neighborState: BlockState,
world: WorldAccess,
pos: BlockPos,
neighborPos: BlockPos
): BlockState {
val isStation = neighborState.isOf(this)
val part = state.get(PART)
if (!isStation && part == ModStationPart.TOP && neighborPos == pos.down()) {
return Blocks.AIR.defaultState
} else if (!isStation && part == ModStationPart.BOTTOM && neighborPos == pos.up()) {
return Blocks.AIR.defaultState
}
return state
}
override fun onUse(
state: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
hand: Hand,
hit: BlockHitResult
): ActionResult {
player.openHandledScreen(state.createScreenHandlerFactory(world, pos))
return ActionResult.CONSUME
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.block.entity.RestorationTankBlockEntity
import com.cobblemon.mod.common.block.multiblock.FossilMultiblockBuilder
import net.minecraft.block.*
import net.minecraft.entity.LivingEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemPlacementContext
@@ -216,6 +217,11 @@ class RestorationTankBlock(properties: Settings) : MultiblockBlock(properties),
override fun asString() = label
}
@Deprecated("Deprecated in Java")
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
return false
}
companion object {
val PART = EnumProperty.of("part", TankPart::class.java)
val TRIGGERED = Properties.TRIGGERED
@@ -0,0 +1,547 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.block.entity.TMBlockEntity
import com.cobblemon.mod.common.gui.TMMScreenHandler
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.*
import net.minecraft.block.entity.BlockEntity
import net.minecraft.block.entity.BrewingStandBlockEntity
import net.minecraft.entity.ItemEntity
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.fluid.FluidState
import net.minecraft.fluid.Fluids
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemPlacementContext
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.registry.Registries
import net.minecraft.screen.NamedScreenHandlerFactory
import net.minecraft.screen.SimpleNamedScreenHandlerFactory
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.server.world.ServerWorld
import net.minecraft.sound.SoundCategory
import net.minecraft.stat.Stats
import net.minecraft.state.StateManager
import net.minecraft.state.property.BooleanProperty
import net.minecraft.state.property.Properties
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
import net.minecraft.world.WorldAccess
class TMBlock(properties: Settings): BlockWithEntity(properties), Waterloggable, InventoryProvider {
/*val FACING = Properties.FACING
val TRIGGERED = Properties.TRIGGERED
val WATERLOGGED = Properties.WATERLOGGED*/
//var filterTM: TechnicalMachine? = null
//var previousFilterTM: TechnicalMachine? = null
var loadedMaterials: MutableList<ItemStack> = mutableListOf()
//val inv = TMBlockInventory(this)
/*class TMBlockInventory(val tmBlock: TMBlock) : SidedInventory {
override fun clear() {
TODO("Not yet implemented")
}
override fun size(): Int {
TODO("Not yet implemented")
}
override fun isEmpty(): Boolean {
return false
}
override fun getStack(slot: Int): ItemStack {
return ItemStack.EMPTY
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
return ItemStack.EMPTY
}
override fun removeStack(slot: Int): ItemStack {
TODO("Not yet implemented")
}
override fun setStack(slot: Int, stack: ItemStack?) {
TODO("Not yet implemented")
}
override fun markDirty() {
TODO("Not yet implemented")
}
override fun canPlayerUse(player: PlayerEntity?): Boolean {
return false
}
override fun getAvailableSlots(side: Direction?): IntArray {
return IntArray(1)
}
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
// todo only allow for hopper to insert materials if it has a filterTM in it
if (tmBlock.filterTM != null && stack != null) {
// if material is needed then load it
if (tmBlock.materialNeeded(tmBlock.filterTM!!, stack)) {
tmBlock.loadMaterial(stack)
return true
}
else
return false
}
return false
}
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
return false
}
}*/
companion object {
//val WATERLOGGED = BooleanProperty.of("waterlogged")
val ON = BooleanProperty.of("on")
val FACING = Properties.FACING
val TRIGGERED = Properties.TRIGGERED
val WATERLOGGED = Properties.WATERLOGGED
private var NORTH_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0, 0.0, 0.0, 1.0, 0.3125, 0.9375),
VoxelShapes.cuboid(0.0, 0.3125, 0.75, 1.0, 0.9375, 0.9375),
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
)
private var SOUTH_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0, 0.0, 0.0625, 1.0, 0.3125, 1.0),
VoxelShapes.cuboid(0.0, 0.3125, 0.0625, 1.0, 0.9375, 0.25),
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
)
private var WEST_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0, 0.0, 0.0, 0.9375, 0.3125, 1.0),
VoxelShapes.cuboid(0.75, 0.3125, 0.0, 0.9375, 0.9375, 1.0),
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
)
private var EAST_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0625, 0.0, 0.0, 1.0, 0.3125, 1.0),
VoxelShapes.cuboid(0.0625, 0.3125, 0.0, 0.25, 0.9375, 1.0),
VoxelShapes.cuboid(0.0625, 0.3125, 0.0625, 0.9375, 0.875, 0.9375)
)
}
init {
defaultState = this.stateManager.defaultState.with(FACING, Direction.NORTH)
.with(WATERLOGGED, false)
.with(ON, false)
.with(TRIGGERED, false)
}
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
return defaultState
.with(FACING, blockPlaceContext.horizontalPlayerFacing)
.with(WATERLOGGED, blockPlaceContext.world.getFluidState(blockPlaceContext.blockPos).fluid == Fluids.WATER)
.with(ON, false)
}
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity?) {
super.onBreak(world, pos, state, player)
// todo dump materials and filterTM on the ground
}
@Deprecated("Deprecated in Java")
override fun neighborUpdate(state: BlockState, world: World, pos: BlockPos, sourceBlock: Block?, sourcePos: BlockPos?, notify: Boolean) {
val bl = world.isReceivingRedstonePower(pos) || world.isReceivingRedstonePower(pos.up())
val bl2 = state.get(TRIGGERED)
if (bl && !bl2) {
world.scheduleBlockTick(pos, this, 4)
world.setBlockState(pos, state.with(TRIGGERED, true) as BlockState, NO_REDRAW)
} else if (!bl && bl2) {
world.setBlockState(pos, state.with(TRIGGERED, false) as BlockState, NO_REDRAW)
}
}
@Deprecated("Deprecated in Java")
override fun scheduledTick(state: BlockState?, world: ServerWorld?, pos: BlockPos?, random: net.minecraft.util.math.random.Random?) {
val inventory = (world!!.getBlockEntity(pos) as TMBlockEntity).tmmInventory
val tmEntity = (world.getBlockEntity(pos) as TMBlockEntity)
if(world == null || pos == null) {
return
}
//val tankEntity = world.getBlockEntity(pos) as MultiblockEntity
if (tmEntity.blockState.get(ON)) {
//print("TMM is in use so scheduled Tick is disabled")
return
}
//this.onTriggerEvent(state, world, pos, random)
// todo if the output slot is empty then try to craft a TM to that slot
if (ItemStack.areItemsEqual(inventory.items!!.get(3), ItemStack.EMPTY)) {
if (state?.let { getInventory(it, world, pos) } != null) {
val itemStack: ItemStack
itemStack = if (inventory.filterTM != null)
inventory.filterTM!!.copy()
else
ItemStack(CobblemonItems.BLANK_TM, 1)
val tm = TechnicalMachines.getTechnicalMachineFromStack(itemStack)
// todo use isReadyToCraftTM to finalize the creation
if (((inventory.filterTM != null && tm != null) && isReadyToCraftTM(state, world, pos, tm)) || isReadyToCraftBlankTM(state, world, pos)) {
// create TM item to the output slot
inventory.items!!.set(3, itemStack)
tmEntity.markDirty()
// Play sound for TM creation
world.playSoundServer(tmEntity.blockPos.toVec3d(), CobblemonSounds.TMM_CRAFT, SoundCategory.BLOCKS)
}
}
}
// todo if there is an item in the output slot then spit it out
else {
if (state?.let { getInventory(it, world, pos) } != null) {
val itemStack = inventory.items!!.get(3)
// Get the direction the block is facing
val facingDirection = state.get(Properties.FACING)?.opposite ?: return
// Calculate the center position of the block
val frontOffset = 0.5 // Half block offset to the front
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
// Create the ItemEntity at the center of the block
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, itemStack).copy()
// Create the ItemEntity
itemEntity.setVelocity(0.0, 0.0, 0.0)
// Add the ItemEntity to the world
world.spawnEntity(itemEntity)
//clear inventory of SidedInventory
inventory.items?.clear()
tmEntity.markDirty()
//getInventory(state, world, pos).clear()
}
}
/*if (state?.let { getInventory(it, world, pos) } != null) {
val itemStack: ItemStack
itemStack = if (inventory.filterTM != null)
inventory.filterTM!!
else
ItemStack(CobblemonItems.BLANK_TM, 1)
val tm = TechnicalMachines.getTechnicalMachineFromStack(itemStack)
// todo use isReadyToCraftTM to finalize the creation
if ((inventory.filterTM != null && isReadyToCraftTM(state, world, pos, tm!!)) || isReadyToCraftBlankTM(state, world, pos)) {
// Get the direction the block is facing
val facingDirection = state.get(Properties.FACING)?.opposite ?: return
// Calculate the center position of the block
val frontOffset = 0.5 // Half block offset to the front
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
// Create the ItemEntity at the center of the block
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, itemStack).copy()
// Create the ItemEntity
itemEntity.setVelocity(0.0, 0.0, 0.0)
// Add the ItemEntity to the world
world.spawnEntity(itemEntity)
//clear inventory of SidedInventory
inventory.items?.clear()
//getInventory(state, world, pos).clear()
}
}*/
}
/*val currentTm = filterTM ?: return sendPacketToServer(CraftBlankTMPacket(handler.input.getStack(2)))
sendPacketToServer(
CraftTMPacket(
currentTm,
handler.input.getStack(0),
handler.input.getStack(1),
handler.input.getStack(2)
)
)*/
@Deprecated("Deprecated in Java")
override fun canPathfindThrough(blockState: BlockState, blockGetter: BlockView, blockPos: BlockPos, pathComputationType: NavigationType) = false
@Deprecated("Deprecated in Java")
override fun hasComparatorOutput(state: BlockState?): Boolean {
return true
}
@Deprecated("Deprecated in Java")
override fun getComparatorOutput(state: BlockState, world: World?, pos: BlockPos?): Int {
if(world == null || pos == null) {
return 0
}
val tmBlockEntity = world.getBlockEntity(pos) as TMBlockEntity
val tm = TechnicalMachines.getTechnicalMachineFromStack(tmBlockEntity.tmmInventory.filterTM)
// if TMM has already crafted something and something is in the output slot do not send signal out
if (!ItemStack.areItemsEqual(tmBlockEntity.tmmInventory.items?.get(3), ItemStack.EMPTY))
return 0
// if the TMM is ready to craft send signal of 15 out
if ((tm != null && isReadyToCraftTM(state, world, pos, tm)) || isReadyToCraftBlankTM(state, world, pos))
return 15
/*if (tmBlockEntity.automationDelay > 0)
tmBlockEntity.automationDelay--
if ((tmBlockEntity.tmmInventory.filterTM != null && isReadyToCraftTM(state, world, pos, tmBlockEntity.tmmInventory.filterTM!!)) || isReadyToCraftBlankTM(state, world, pos)) {
if (tmBlockEntity.automationDelay == 0) {
tmBlockEntity.resetAutomationDelay()
return 15
}
else
return 0
}*/
return 0
}
override fun getInventory(
state: BlockState,
world: WorldAccess,
pos: BlockPos
): SidedInventory {
val tmBlockEntity = world.getBlockEntity(pos) as TMBlockEntity
return tmBlockEntity.tmmInventory
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
builder.add(FACING)
builder.add(WATERLOGGED)
builder.add(ON)
builder.add(TRIGGERED)
}
override fun createBlockEntity(pos: BlockPos, state: BlockState): BlockEntity {
return TMBlockEntity(pos, state)
}
@Deprecated("Deprecated in Java")
override fun rotate(blockState: BlockState, rotation: BlockRotation) =
blockState.with(FACING, rotation.rotate(blockState.get(FACING)))
@Deprecated("Deprecated in Java")
override fun mirror(blockState: BlockState, mirror: BlockMirror): BlockState {
return blockState.rotate(mirror.getRotation(blockState.get(FACING)))
}
@Deprecated("Deprecated in Java")
override fun onStateReplaced(state: BlockState, world: World, pos: BlockPos?, newState: BlockState, moved: Boolean) {
if (!state.isOf(newState.block)) super.onStateReplaced(state, world, pos, newState, moved)
}
@Deprecated("Deprecated in Java")
override fun onUse(
blockState: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
interactionHand: Hand,
blockHitResult: BlockHitResult
): ActionResult {
// clear filter TM on client
if (world.isClient) {
val tmBlockEntity = world.getBlockEntity(pos)
val inventory = (tmBlockEntity as TMBlockEntity).tmmInventory
inventory.filterTM = null
return ActionResult.SUCCESS
}
val tmBlockEntity = world.getBlockEntity(pos)
if (tmBlockEntity is TMBlockEntity) {
// Set the block state to ON when the block is used
tmBlockEntity.stateManager.openContainer(player, world, pos, blockState)
player.openHandledScreen(tmBlockEntity as TMBlockEntity?)
}
val inventory = (tmBlockEntity as TMBlockEntity).tmmInventory // (world.getBlockEntity(pos) as TMBlockEntity).tmmInventory
// remove all machine from machine upon use
// inventory.items?.forEach {
// // Get the direction the block is facing
// val facingDirection = blockState.get(Properties.FACING).opposite
//
// // Calculate the center position of the block
// val frontOffset = 0.5 // Half block offset to the front
// val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
// val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
// val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
//
// // Create the ItemEntity at the center of the block
// val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, it)
//
// // Create the ItemEntity
// itemEntity.setVelocity(0.0, 0.0, 0.0)
//
// // Add the ItemEntity to the world
// world.spawnEntity(itemEntity)
// }
//
// inventory.items?.clear()
// spit out any disc filters
if (inventory.filterTM != null) {
// spit out the filter TM and set filterTM to null
val filterTMStack = inventory.filterTM!!
// Get the direction the block is facing
val facingDirection = blockState.get(Properties.FACING).opposite
// Calculate the center position of the block
val frontOffset = 0.5 // Half block offset to the front
val spawnX = pos.x + 0.5 + facingDirection.offsetX * frontOffset
val spawnY = pos.y + 0.3 + facingDirection.offsetY * frontOffset
val spawnZ = pos.z + 0.5 + facingDirection.offsetZ * frontOffset
// Create the ItemEntity at the center of the block
val itemEntity = ItemEntity(world, spawnX, spawnY, spawnZ, filterTMStack).copy()
// Create the ItemEntity
itemEntity.setVelocity(0.0, 0.0, 0.0)
// Add the ItemEntity to the world
world.spawnEntity(itemEntity)
inventory.filterTM = null
}
val blockEntity = world.getBlockEntity(pos)
if (blockEntity is TMBlockEntity) {
//player.openHandledScreen(blockEntity as TMBlockEntity?)
player.playSound(CobblemonSounds.TMM_ON, SoundCategory.BLOCKS, 1.0f, 1.0f)
// todo maybe uncomment this
//val serverPlayer = player as ServerPlayerEntity
//serverPlayer.openHandledScreen(blockState.createScreenHandlerFactory(world, pos))
}
return ActionResult.CONSUME
}
// override fun createScreenHandlerFactory(
// state: BlockState?,
// world: World?,
// pos: BlockPos?
// ): NamedScreenHandlerFactory {
// return SimpleNamedScreenHandlerFactory(::TMMScreenHandler, Text.of("TM Machine"))
// }
/*fun loadMaterial(itemStack: ItemStack) {
loadedMaterials.add(itemStack)
}*/
fun isReadyToCraftTM(state: BlockState?, world: WorldAccess, pos: BlockPos, tm: TechnicalMachine): Boolean {
val typeGem = Registries.ITEM.get(tm.let { ElementalTypes.get(it.type)?.typeGem }).defaultStack
val recipeItem = Registries.ITEM.get(tm.recipe?.item)
val inventory = state?.let { getInventory(it, world, pos) }
return (inventory!!.count(CobblemonItems.BLANK_TM) == 1
&& inventory.count(typeGem.item) == 1
&& ((inventory.count(recipeItem) == tm.recipe?.count) || (ItemStack.areItemsEqual(recipeItem.defaultStack, ItemStack.EMPTY))))
}
fun isReadyToCraftBlankTM(state: BlockState?, world: WorldAccess, pos: BlockPos): Boolean {
val inventory = state?.let { getInventory(it, world, pos) }
return (inventory!!.count(Items.AMETHYST_SHARD) == 1)
}
/*fun materialNeeded(tm: TechnicalMachine, itemStack: ItemStack): Boolean {
val typeGem = Registries.ITEM.get(ElementalTypes.get(tm.type)?.typeGem).defaultStack
val recipeItem = Registries.ITEM.get(tm.recipe?.item)
// if blank TM is needed still
if ((itemStack !in loadedMaterials && itemStack == CobblemonItems.BLANK_TM.defaultStack))
return true // load in blank TM
// if type gem is needed still
else if ((itemStack !in loadedMaterials && itemStack == typeGem))
return true // load in Type Gem
// if the itemStack is listed as needed in the recipe and the count is not currently met
else if (itemStack.item == recipeItem && loadedMaterials.count { it == itemStack } < tm.recipe?.count!!)
return true
else
return false
}*/
/*fun clearLoadedMaterials() {
loadedMaterials.clear()
}*/
@Deprecated("Deprecated in Java")
override fun getRenderType(blockState: BlockState): BlockRenderType {
return BlockRenderType.MODEL
}
override fun getFluidState(state: BlockState): FluidState? {
return if (state.get(WATERLOGGED)) {
Fluids.WATER.getStill(false)
} else super.getFluidState(state)
}
override fun getOutlineShape(
state: BlockState,
world: BlockView,
pos: BlockPos,
context: ShapeContext
): VoxelShape {
return when (state.get(FACING)) {
Direction.NORTH -> NORTH_OUTLINE
Direction.SOUTH -> SOUTH_OUTLINE
Direction.WEST -> WEST_OUTLINE
else -> EAST_OUTLINE
}
}
}
@@ -0,0 +1,20 @@
package com.cobblemon.mod.common.block
import net.minecraft.block.*
import net.minecraft.util.math.BlockPos
import net.minecraft.world.BlockView
class TypeGemBlock(
settings: Settings,
stage: Int,
height: Int,
xzOffset: Int,
nextStage: Block?,
val baseBlock: Block
) : GrowableStoneBlock(settings, stage, height, xzOffset, nextStage) {
override fun canGrow(pos: BlockPos, world: BlockView): Boolean {
val state = world.getBlockState(pos)
val dir = state.get(FACING).opposite
return (world.getBlockState(pos.offset(dir)).block == baseBlock)
}
}
@@ -0,0 +1,228 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.chest
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.block.entity.GildedChestBlockEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.toVec3d
import net.minecraft.block.Block
import net.minecraft.block.BlockRenderType
import net.minecraft.block.BlockState
import net.minecraft.block.BlockWithEntity
import net.minecraft.block.Blocks
import net.minecraft.block.HorizontalFacingBlock
import net.minecraft.block.ShapeContext
import net.minecraft.entity.ai.pathing.NavigationType
import net.minecraft.entity.mob.PiglinBrain
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.item.ItemPlacementContext
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
import net.minecraft.state.StateManager
import net.minecraft.state.property.Properties
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.ActionResult
import net.minecraft.util.BlockMirror
import net.minecraft.util.BlockRotation
import net.minecraft.util.Hand
import net.minecraft.util.Identifier
import net.minecraft.util.ItemScatterer
import net.minecraft.util.hit.BlockHitResult
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.util.shape.VoxelShape
import net.minecraft.util.shape.VoxelShapes
import net.minecraft.world.BlockView
import net.minecraft.world.World
@Suppress("OVERRIDE_DEPRECATION")
class GildedChestBlock(settings: Settings, val type: Type = Type.RED) : BlockWithEntity(settings) {
init {
defaultState = defaultState.with(Properties.HORIZONTAL_FACING, Direction.SOUTH)
}
companion object {
val POKEMON_ARGS = "gimmighoul"
val LEVEL_RANGE = 5..30
val SOUTH_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0, 0.0, 0.25, 1.0, 1.0, 0.9375)
)
val NORTH_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0, 0.0, 0.0625, 1.0, 1.0, 0.75)
)
val WEST_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.0625, 0.0, 0.0, 0.75, 1.0, 1.0)
)
val EAST_OUTLINE = VoxelShapes.union(
VoxelShapes.cuboid(0.25, 0.0, 0.0, 0.9375, 1.0, 1.0)
)
}
override fun createBlockEntity(pos: BlockPos, state: BlockState) = GildedChestBlockEntity(pos, state, type)
override fun getOutlineShape(
state: BlockState,
world: BlockView,
pos: BlockPos,
context: ShapeContext
): VoxelShape {
return when (state.get(HorizontalFacingBlock.FACING)) {
Direction.NORTH -> NORTH_OUTLINE
Direction.SOUTH -> SOUTH_OUTLINE
Direction.WEST -> WEST_OUTLINE
else -> EAST_OUTLINE
}
}
override fun appendProperties(builder: StateManager.Builder<Block, BlockState>) {
super.appendProperties(builder)
builder.add(Properties.HORIZONTAL_FACING)
}
private val facingToYaw: HashMap<Direction, Float> = hashMapOf(
Direction.NORTH to -179.0F,
Direction.WEST to 90.0F,
Direction.SOUTH to 0.0F,
Direction.EAST to -90.0F
)
override fun getName(): MutableText {
return if (isFake()) Text.translatable("block.cobblemon.gilded_chest") else super.getName()
}
fun isFake() = (type == Type.FAKE)
override fun onBreak(world: World, pos: BlockPos, state: BlockState, player: PlayerEntity) {
if (isFake()) {
if (player is ServerPlayerEntity) {
spawnPokemon(world, pos, state, player)
}
world.setBlockState(pos, Blocks.AIR.defaultState)
} else super.onBreak(world, pos, state, player)
}
private fun spawnPokemon(world: World, pos: BlockPos, state: BlockState, player: ServerPlayerEntity) : ActionResult {
val properties = "$POKEMON_ARGS lvl=${LEVEL_RANGE.random()}"
val pokemon = PokemonProperties.parse(properties)
val entity = pokemon.createEntity(world)
// The yaw based on the block's facing direction
val yaw = facingToYaw[state[HorizontalFacingBlock.FACING]] ?: 0.0F
entity.dataTracker.set(PokemonEntity.SPAWN_DIRECTION, facingToYaw[state[HorizontalFacingBlock.FACING]])
val offsetDir = state[HorizontalFacingBlock.FACING]
val vec = pos.toVec3d().add(offsetDir.offsetX * 0.1 + 0.5, 0.0, offsetDir.offsetZ * 0.1 + 0.5)
entity.refreshPositionAndAngles(vec.x, vec.y, vec.z, yaw, entity.pitch)
world.spawnEntity(entity)
world.removeBlock(pos, false)
afterOnServer(ticks = 2) {
if (player !in player.world.players) {
return@afterOnServer
}
val party = player.party()
if (!player.isCreative) {
entity.forceBattle(player)
} else {
world.playSound(null, pos, CobblemonSounds.GIMMIGHOUL_REVEAL, SoundCategory.NEUTRAL)
}
}
return ActionResult.SUCCESS
}
override fun onUse(
state: BlockState,
world: World,
pos: BlockPos,
player: PlayerEntity,
hand: Hand,
hit: BlockHitResult
): ActionResult {
if (isFake()) {
if (player is ServerPlayerEntity) {
return spawnPokemon(world, pos, state, player)
} else {
return ActionResult.SUCCESS
}
}
val entity = world.getBlockEntity(pos) as? GildedChestBlockEntity ?: return ActionResult.FAIL
if (world.getBlockState(pos.up()).isSolidBlock(world, pos.up())) return ActionResult.FAIL
player.openHandledScreen(entity)
if (!player.world.isClient) {
PiglinBrain.onGuardedBlockInteracted(player, true)
}
val state = entity.poseableState
state.currentModel?.let {
it.moveToPose(null, state, it.getPose("OPEN")!!)
}
return ActionResult.SUCCESS
}
override fun onStateReplaced(
state: BlockState,
world: World,
pos: BlockPos,
newState: BlockState,
moved: Boolean
) {
if (!state.isOf(newState.block)) {
val chest = world.getBlockEntity(pos) as GildedChestBlockEntity
ItemScatterer.spawn(world, pos, chest.inventoryContents)
}
}
override fun getRenderType(state: BlockState?) = BlockRenderType.ENTITYBLOCK_ANIMATED
override fun getPlacementState(blockPlaceContext: ItemPlacementContext): BlockState? {
// val abovePosition = blockPlaceContext.blockPos.up()
// val world = blockPlaceContext.world
// if (world.getBlockState(abovePosition).canReplace(blockPlaceContext) && !world.isOutOfHeightLimit(abovePosition)) {
return defaultState.with(HorizontalFacingBlock.FACING, blockPlaceContext.horizontalPlayerFacing.opposite)
// }
// return null
}
@Deprecated("Deprecated in Java")
override fun rotate(state: BlockState, rotation: BlockRotation): BlockState {
return state.with(
Properties.HORIZONTAL_FACING, rotation.rotate(
state.get(Properties.HORIZONTAL_FACING) as Direction
)
) as BlockState
}
override fun mirror(state: BlockState, mirror: BlockMirror): BlockState {
return state.rotate(mirror.getRotation(state.get(Properties.HORIZONTAL_FACING) as Direction))
}
enum class Type(val poserId: Identifier) {
RED(cobblemonResource("gilded_chest")),
BLUE(cobblemonResource("blue_gilded_chest")),
GREEN(cobblemonResource("green_gilded_chest")),
PINK(cobblemonResource("pink_gilded_chest")),
WHITE(cobblemonResource("white_gilded_chest")),
BLACK(cobblemonResource("black_gilded_chest")),
YELLOW(cobblemonResource("yellow_gilded_chest")),
FAKE(cobblemonResource("gilded_chest"))
}
@Deprecated("Deprecated in Java")
override fun canPathfindThrough(state: BlockState?, world: BlockView?, pos: BlockPos?, type: NavigationType?): Boolean {
return false
}
}
@@ -0,0 +1,27 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.chest
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
import com.cobblemon.mod.common.client.render.models.blockbench.PoseableEntityState
import net.minecraft.entity.Entity
class GildedState : PoseableEntityState<Entity>() {
override fun getEntity() = null
init {
setPose("CLOSED")
}
override fun updatePartialTicks(partialTicks: Float) {
this.currentPartialTicks += partialTicks
}
override val schedulingTracker = SchedulingTracker()
}
@@ -0,0 +1,180 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntity
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
import net.minecraft.sound.SoundCategory
import net.minecraft.util.ActionResult
import net.minecraft.util.Hand
import net.minecraft.util.collection.DefaultedList
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
class DisplayCaseBlockEntity(pos: BlockPos, state: BlockState) : BlockEntity(CobblemonBlockEntities.DISPLAY_CASE, pos, state), SidedInventory {
val inv: DefaultedList<ItemStack> = DefaultedList.ofSize(1, ItemStack.EMPTY)
/**
* Updates the [ItemStack] stored in this entity
*
* @param player The [PlayerEntity] that performed this interaction
* @param hand The [Hand] the player used to perform this interaction
* @author whatsy
*/
fun updateItem(player: PlayerEntity, hand: Hand): ActionResult {
val playerStack = player.getStackInHand(hand)
// Player and case item are the same - do nothing
if (playerStack.item == getStack().item) {
return if (playerStack.item != Items.AIR) ActionResult.SUCCESS else ActionResult.FAIL
}
// Player's hand is empty, case is not empty - give player the item in the case
if (playerStack.isEmpty && !getStack().isEmpty) {
if (!player.isCreative) player.setStackInHand(hand, getStack())
setCaseStack(ItemStack.EMPTY)
return ActionResult.success(true)
}
// Case is empty, player's hand is not - put playerStack in the case
if (getStack().isEmpty && !playerStack.isEmpty) {
setCaseStack(playerStack.copy())
if (!player.isCreative) playerStack.decrement(1)
return ActionResult.success(true)
}
// Player has item, case has item - swap items
if (!getStack().isEmpty && !playerStack.isEmpty) {
val oldCaseStack = getStack()
setCaseStack(playerStack.copy())
if (!player.isCreative) {
playerStack.decrement(1)
player.giveItemStack(oldCaseStack)
}
return ActionResult.SUCCESS
}
return ActionResult.FAIL
}
fun getStack(): ItemStack {
return inv[0]
}
private fun setCaseStack(newStack: ItemStack) {
if (world == null) return
val oldState = world!!.getBlockState(pos)
newStack.count = 1
inv[0] = newStack
if (newStack.isEmpty) {
world!!.playSound(null, pos, CobblemonSounds.DISPLAY_CASE_REMOVE_ITEM, SoundCategory.BLOCKS)
} else {
world!!.playSound(null, pos, CobblemonSounds.DISPLAY_CASE_ADD_ITEM, SoundCategory.BLOCKS)
}
world!!.updateListeners(pos, oldState, world!!.getBlockState(pos), Block.NOTIFY_LISTENERS)
world!!.updateComparators(pos, world!!.getBlockState(pos).block)
}
override fun writeNbt(nbt: NbtCompound) {
super.writeNbt(nbt)
Inventories.writeNbt(nbt, inv, true)
}
override fun readNbt(nbt: NbtCompound) {
super.readNbt(nbt)
inv.clear()
Inventories.readNbt(nbt, inv)
}
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
return BlockEntityUpdateS2CPacket.create(this)
}
override fun toInitialChunkDataNbt(): NbtCompound {
return this.createNbt()
}
private fun onItemUpdated(world: World, oldState: BlockState, newState: BlockState) {
markDirty()
world.updateListeners(pos, oldState, newState, Block.NOTIFY_LISTENERS)
world.updateComparators(pos, world.getBlockState(pos).block)
}
override fun clear() {
inv.clear()
}
override fun size(): Int {
return inv.size
}
override fun isEmpty(): Boolean {
return getStack().isEmpty
}
override fun getStack(slot: Int): ItemStack {
return getStack()
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
val oldState = cachedState
val result = Inventories.splitStack(inv, slot, amount)
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
return result
}
override fun removeStack(slot: Int): ItemStack {
val oldState = cachedState
val result = Inventories.removeStack(inv, slot)
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
return result
}
override fun setStack(slot: Int, stack: ItemStack) {
val oldState = cachedState
inv[slot] = stack
if (stack.count > stack.maxCount) {
stack.count = stack.maxCount
}
if (world != null) onItemUpdated(world!!, oldState, world!!.getBlockState(pos))
}
override fun canPlayerUse(player: PlayerEntity?) = false
override fun getAvailableSlots(side: Direction?): IntArray {
val result = IntArray(inv.size)
for (i in result.indices) {
result[i] = i
}
return result
}
override fun getMaxCountPerStack() = 1
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
if(dir == Direction.DOWN) return false
return getStack().isEmpty
}
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean = (dir == Direction.DOWN)
}
@@ -0,0 +1,149 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.block.chest.GildedChestBlock.Type
import com.cobblemon.mod.common.block.chest.GildedState
import net.minecraft.block.BlockState
import net.minecraft.block.entity.BlockEntityType
import net.minecraft.block.entity.LootableContainerBlockEntity
import net.minecraft.block.entity.ViewerCountManager
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.DoubleInventory
import net.minecraft.inventory.Inventories
import net.minecraft.item.ItemStack
import net.minecraft.nbt.NbtCompound
import net.minecraft.screen.GenericContainerScreenHandler
import net.minecraft.sound.SoundCategory
import net.minecraft.state.property.Properties
import net.minecraft.text.Text
import net.minecraft.util.collection.DefaultedList
import net.minecraft.util.math.BlockPos
import net.minecraft.world.World
class GildedChestBlockEntity(pos: BlockPos, state: BlockState, val type: Type = Type.RED) : LootableContainerBlockEntity(CobblemonBlockEntities.GILDED_CHEST, pos, state) {
var inventoryContents: DefaultedList<ItemStack> = DefaultedList.ofSize(NUM_SLOTS, ItemStack.EMPTY)
val poseableState: GildedState = GildedState()
private val stateManager: ViewerCountManager = object : ViewerCountManager() {
override fun onContainerOpen(world: World, pos: BlockPos, state: BlockState) {
playSound(world, pos, state, CobblemonSounds.GILDED_CHEST_OPEN)
}
override fun onContainerClose(world: World, pos: BlockPos, state: BlockState) {
playSound(world, pos, state, CobblemonSounds.GILDED_CHEST_CLOSE)
}
override fun onViewerCountUpdate(
world: World,
pos: BlockPos,
state: BlockState,
oldViewerCount: Int,
newViewerCount: Int
) {
this@GildedChestBlockEntity.onViewerCountUpdate(world, pos, state, oldViewerCount, newViewerCount)
}
override fun isPlayerViewing(player: PlayerEntity): Boolean {
if (player.currentScreenHandler is GenericContainerScreenHandler) {
val inventory = (player.currentScreenHandler as GenericContainerScreenHandler).inventory
return inventory === this@GildedChestBlockEntity || inventory is DoubleInventory && inventory.isPart(
this@GildedChestBlockEntity
)
}
return false
}
}
override fun getType(): BlockEntityType<*> = CobblemonBlockEntities.GILDED_CHEST
override fun size() = NUM_SLOTS
override fun getContainerName() = Text.translatable("block.cobblemon.gilded_chest")
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?) =
GenericContainerScreenHandler.createGeneric9x3(syncId, playerInventory, this);
override fun onOpen(player: PlayerEntity) {
if (!this.removed && !player.isSpectator && type != Type.FAKE) {
stateManager.openContainer(player, this.getWorld(), this.getPos(), this.cachedState)
}
}
override fun onClose(player: PlayerEntity) {
if (!this.removed && !player.isSpectator) {
stateManager.closeContainer(player, this.getWorld(), this.getPos(), this.cachedState)
}
}
override fun getInvStackList(): DefaultedList<ItemStack> = inventoryContents
override fun setInvStackList(list: DefaultedList<ItemStack>) {
inventoryContents = list
}
companion object {
val NUM_SLOTS = 27
fun playSound(world: World, pos: BlockPos, state: BlockState, sound: net.minecraft.sound.SoundEvent) {
var d = pos.x.toDouble() + 0.5
val e = pos.y.toDouble() + 0.5
var f = pos.z.toDouble() + 0.5
val direction = state.get(Properties.HORIZONTAL_FACING)
d += direction.offsetX.toDouble() * 0.5
f += direction.offsetZ.toDouble() * 0.5
world.playSound(
null,
d,
e,
f,
sound,
SoundCategory.BLOCKS,
0.5f,
world.random.nextFloat() * 0.1f + 0.9f
)
}
}
override fun onSyncedBlockEvent(type: Int, data: Int): Boolean {
if (type == 1) {
val isNowOpen = data > 0
val wasOpen = poseableState.currentPose == "OPEN"
val model = poseableState.currentModel ?: return true
if (isNowOpen && !wasOpen) {
model.moveToPose(null, poseableState, model.getPose("OPEN")!!)
} else if (!isNowOpen && wasOpen) {
model.moveToPose(null, poseableState, model.getPose("CLOSED")!!)
}
return true
}
return super.onSyncedBlockEvent(type, data)
}
fun onViewerCountUpdate(world: World, pos: BlockPos, state: BlockState, oldViewerCount: Int, newViewerCount: Int) {
val block = state.block
world.addSyncedBlockEvent(pos, block, 1, newViewerCount)
}
override fun writeNbt(nbt: NbtCompound?) {
super.writeNbt(nbt)
if (!serializeLootTable(nbt)) {
Inventories.writeNbt(nbt, inventoryContents)
}
}
override fun readNbt(nbt: NbtCompound?) {
super.readNbt(nbt)
inventoryContents= DefaultedList.ofSize(
size(), ItemStack.EMPTY
)
if (!deserializeLootTable(nbt)) {
Inventories.readNbt(nbt, inventoryContents)
}
}
}
@@ -128,7 +128,7 @@ class RestorationTankBlockEntity(
val canUtilize = stack?.let { NaturalMaterials.isNaturalMaterial(it) } == true
&& structure.organicMaterialInside < FossilMultiblockStructure.MATERIAL_TO_START
&& structure.createdPokemon == null
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return false
val returnItem = NaturalMaterials.getReturnItem(stack!!) ?: return canUtilize
if(canUtilize) {
// See if there's room
for (i in items.indices ) {
@@ -0,0 +1,349 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.block.entity
import com.cobblemon.mod.common.CobblemonBlockEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.block.TMBlock
import com.cobblemon.mod.common.gui.TMMScreenHandler
import net.minecraft.block.Block
import net.minecraft.block.BlockState
import net.minecraft.block.entity.LockableContainerBlockEntity
import net.minecraft.block.entity.ViewerCountManager
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.inventory.Inventories
import net.minecraft.inventory.Inventory
import net.minecraft.inventory.SidedInventory
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.nbt.NbtCompound
import net.minecraft.network.listener.ClientPlayPacketListener
import net.minecraft.network.packet.Packet
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket
import net.minecraft.registry.Registries
import net.minecraft.screen.ScreenHandler
import net.minecraft.text.Text
import net.minecraft.util.collection.DefaultedList
import net.minecraft.util.math.BlockPos
import net.minecraft.util.math.Direction
import net.minecraft.world.World
class TMBlockEntity(
val blockPos: BlockPos,
val blockState: BlockState
) : LockableContainerBlockEntity(CobblemonBlockEntities.TM_BLOCK, blockPos, blockState) {
var tmmInventory = TMBlockInventory(this)
var automationDelay: Int = AUTOMATION_DELAY
var partialTicks = 0.0f
val stateManager: ViewerCountManager = object : ViewerCountManager() {
override fun onContainerOpen(world: World, pos: BlockPos, state: BlockState) {
this@TMBlockEntity.setOpen(state, true)
}
override fun onContainerClose(world: World, pos: BlockPos, state: BlockState) {
this@TMBlockEntity.setOpen(state, false)
}
override fun onViewerCountUpdate(world: World, pos: BlockPos, state: BlockState, oldViewerCount: Int, newViewerCount: Int) {}
override fun isPlayerViewing(player: PlayerEntity): Boolean {
if (player.currentScreenHandler is TMMScreenHandler) {
val inventory = (player.currentScreenHandler as TMMScreenHandler).inventory
return inventory === this@TMBlockEntity
}
return false
}
}
companion object {
const val AUTOMATION_DELAY = 4
const val FILTER_TM_NBT = "FilterTM"
}
fun setOpen(state: BlockState, open: Boolean) {
world!!.setBlockState(getPos(), state.with(TMBlock.ON, open), Block.NOTIFY_ALL)
}
fun tick() {
if (!removed) {
stateManager.updateViewerCount(getWorld(), getPos(), cachedState)
}
}
//var filterTM: TechnicalMachine? = null
//private var inventory: DefaultedList<ItemStack> = DefaultedList.ofSize(size(), ItemStack.EMPTY)
/*override fun size(): Int { return 3 }
override fun getContainerName(): Text {
return Text.translatable("container.tmblock")
}
override fun getInvStackList(): DefaultedList<ItemStack> {
return inventory
}
override fun setInvStackList(list: DefaultedList<ItemStack>?) {
if (list != null) {
this.inventory = list
}
}*/
fun resetAutomationDelay() {
automationDelay = AUTOMATION_DELAY
}
fun materialNeeded(tm: TechnicalMachine?, slot: Int, itemStack: ItemStack): Boolean {
val typeGem = Registries.ITEM.get(tm?.type?.let { ElementalTypes.get(it)?.typeGem }).defaultStack
val recipeItem = Registries.ITEM.get(tm?.recipe?.item)
//val inventory = tmmInventory.inventory
val inventory = tmmInventory.items
//inventory.count { it.value == CobblemonItems.BLANK_TM.defaultStack }
//ItemStack.areEqual()
if (inventory != null) {
if (tmmInventory.filterTM != null) {
// if blank TM is needed still
if (slot == 0 && (inventory.none { it?.count == 1 && it.item == CobblemonItems.BLANK_TM }) && ItemStack.areEqual(itemStack, ItemStack(CobblemonItems.BLANK_TM, 1)))
return true // load in blank TM
// if type gem is needed still
else if (slot == 1 && inventory.none { ItemStack.areEqual(it, typeGem) } && ItemStack.areEqual(itemStack, typeGem))
return true // load in Type Gem
// if the itemStack is listed as needed in the recipe and the count is not currently met
else if (slot == 2 && inventory.none { it?.count == tm?.recipe?.count && ItemStack.areItemsEqual(it, ItemStack(recipeItem, 1)) } && ItemStack.areEqual(itemStack, ItemStack(recipeItem, 1)))
return true
else
return false
}
else if (tmmInventory.filterTM == null && ItemStack.areItemsEqual(itemStack,Items.AMETHYST_SHARD.defaultStack)) {
return (slot == 2 && inventory.none { it?.count == 1 && ItemStack.areItemsEqual(it, Items.AMETHYST_SHARD.defaultStack) })
}
}
return false
}
override fun toInitialChunkDataNbt(): NbtCompound {
val res = NbtCompound()
writeNbt(res)
return res
}
override fun clear() {
tmmInventory.clear()
}
override fun size(): Int {
return this.tmmInventory.size()
}
override fun isEmpty(): Boolean {
return this.tmmInventory.isEmpty()
}
override fun getStack(slot: Int): ItemStack {
return this.tmmInventory.getStack(slot)
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
return this.tmmInventory.removeStack(slot, amount)
}
override fun removeStack(slot: Int): ItemStack {
return this.tmmInventory.removeStack(slot)
}
override fun setStack(slot: Int, stack: ItemStack?) {
return this.tmmInventory.setStack(slot, stack)
}
override fun canPlayerUse(player: PlayerEntity?): Boolean {
return this.tmmInventory.canPlayerUse(player)
}
override fun getContainerName(): Text {
return Text.translatable("container.brewing")
}
override fun createScreenHandler(syncId: Int, playerInventory: PlayerInventory?): ScreenHandler {
return TMMScreenHandler(syncId, playerInventory!!, this.tmmInventory, this)
}
override fun toUpdatePacket(): Packet<ClientPlayPacketListener>? {
return BlockEntityUpdateS2CPacket.create(this)
}
override fun writeNbt(nbt: NbtCompound) {
super.writeNbt(nbt)
Inventories.writeNbt(nbt, tmmInventory.items)
val filterTmCompound = tmmInventory.filterTM?.writeNbt(NbtCompound())
if (filterTmCompound != null)
nbt.put(FILTER_TM_NBT, filterTmCompound)
}
override fun readNbt(nbt: NbtCompound) {
super.readNbt(nbt)
Inventories.readNbt(nbt, tmmInventory.items)
if (nbt.contains(FILTER_TM_NBT)) {
val itemStack = CobblemonItems.TECHNICAL_MACHINE.defaultStack
tmmInventory.filterTM = ItemStack.fromNbt(nbt.getCompound(FILTER_TM_NBT))
val test = 2
}
}
override fun markDirty() {
super.markDirty()
if (this.world != null && !this.world!!.isClient) {
val currentState = world!!.getBlockState(pos)
world!!.setBlockState(pos, currentState, 3) // Flags: 2 | 1 = Block update | Render update
this.world!!.updateNeighborsAlways(this.pos, this.cachedState.block)
}
}
class TMBlockInventory(val tmBlockEntity: TMBlockEntity) : SidedInventory {
private val BLANK_DISC_SLOT_INDEX = 0
private val GEM_SLOT_INDEX = 1
private val MISC_SLOT_INDEX = 2
private val OUTPUT_SLOT_INDEX = 3
private val INPUT_SLOTS = intArrayOf(0, 1, 2, 3)
val entityPos = this.tmBlockEntity.blockPos
val entityState = this.tmBlockEntity.blockState
val entityWorld = this.tmBlockEntity.world
var filterTM: ItemStack? = null
//var inventory: MutableMap<Int, ItemStack?> = mutableMapOf(0 to ItemStack.EMPTY, 1 to ItemStack.EMPTY, 2 to ItemStack.EMPTY)
var items: DefaultedList<ItemStack?>? = DefaultedList.ofSize(4, ItemStack.EMPTY)
fun hasFilterTM(): Boolean {
return filterTM != null
}
override fun clear() {
tmBlockEntity.markDirty()
this.items?.clear()
}
override fun size(): Int {
return this.items?.size ?: 0;
}
/*fun getInvSize(): Int {
return size()
}*/
fun getInvStack(slot: Int): ItemStack {
return items?.get(slot) ?: ItemStack.EMPTY
}
override fun isEmpty(): Boolean {
for (i in 0 until size()) {
val stack: ItemStack = getInvStack(i)
if (!stack.isEmpty) {
return false
}
}
return true
}
/*override fun isEmpty(): Boolean {
return !inventory.any { it.value != ItemStack.EMPTY }
}*/
override fun getStack(slot: Int): ItemStack {
return items?.get(slot) ?: ItemStack.EMPTY
}
override fun getAvailableSlots(side: Direction?): IntArray? {
return if (side == Direction.DOWN) {
return listOf(this.OUTPUT_SLOT_INDEX).toIntArray()
}
else this.INPUT_SLOTS
// val result = this.items?.size?.let { IntArray(it) }
// if (result != null) {
// for (i in result.indices) {
// result[i] = i
// }
// }
// return result
}
override fun removeStack(slot: Int, amount: Int): ItemStack {
tmBlockEntity.markDirty()
return Inventories.splitStack(items, slot, amount)
}
override fun removeStack(slot: Int): ItemStack {
val slotStack = items?.get(slot)
items?.set(slot, ItemStack.EMPTY)
tmBlockEntity.markDirty()
return slotStack ?: ItemStack.EMPTY
}
override fun setStack(slot: Int, stack: ItemStack?) {
items?.set(slot, stack)
tmBlockEntity.markDirty()
}
override fun markDirty() {
if (tmBlockEntity.world != null)
tmBlockEntity.markDirty()
}
override fun canPlayerUse(player: PlayerEntity?): Boolean {
return Inventory.canPlayerUse(this.tmBlockEntity, player)
}
/*override fun getAvailableSlots(side: Direction?): IntArray {
return IntArray(3)
}*/
override fun canInsert(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
if (this.entityState.get(TMBlock.ON)) {
// print("TMM is in use so canInsert is disabled")
return false
}
if (stack != null && slot < this.INPUT_SLOTS.size) {
val tm = TechnicalMachines.getTechnicalMachineFromStack(tmBlockEntity.tmmInventory.filterTM)
// if material is needed then load it
if (tmBlockEntity.materialNeeded(tm, slot, stack) && (ItemStack.areEqual(items?.get(slot) ?: ItemStack.EMPTY, ItemStack.EMPTY) || ItemStack.areItemsEqual(items?.get(slot) ?: ItemStack.EMPTY, stack))) {
//tmBlockEntity.loadMaterial(stack)
return true
}
else
return false
}
return false
}
override fun canExtract(slot: Int, stack: ItemStack?, dir: Direction?): Boolean {
if (this.entityState.get(TMBlock.ON)) {
//print("TMM is in use so canExtract is disabled")
return false
}
return dir == Direction.DOWN && slot == this.OUTPUT_SLOT_INDEX
}
}
}
@@ -328,8 +328,8 @@ class FossilMultiblockStructure (
tankBaseEntity.masterBlockPos = null
tankTopEntity.masterBlockPos = null
// Drop fossils from machine as long as the machine is not running
if (this.timeRemaining == TIME_TO_TAKE || this.timeRemaining == -1) {
// Drop fossils from machine as long as the machine is not started or near completion
if (this.timeRemaining == -1 || this.timeRemaining >= 20) {
this.fossilInventory.forEach {
val stack = ItemStack(it.item, 1)
ItemScatterer.spawn(world, pos.x.toDouble(), pos.y.toDouble(), pos.z.toDouble(), stack)
@@ -62,6 +62,7 @@ object CobblemonBakingOverrides {
cobblemonModel("restoration_tank_connector", "none")
)
fun registerOverride(modelLocation: Identifier, modelIdentifier: ModelIdentifier): BakingOverride {
val result = BakingOverride(modelLocation, modelIdentifier)
models.add(result)
@@ -18,20 +18,28 @@ import com.cobblemon.mod.common.CobblemonEntities
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
import com.cobblemon.mod.common.api.text.gray
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.gui.modstation.ModStationHandledScreen
import com.cobblemon.mod.common.client.gui.tm.TMMHandledScreen
import com.cobblemon.mod.common.client.particle.BedrockParticleEffectRepository
import com.cobblemon.mod.common.client.render.block.BerryBlockRenderer
import com.cobblemon.mod.common.client.render.block.FossilAnalyzerRenderer
import com.cobblemon.mod.common.client.render.block.RestorationTankRenderer
import com.cobblemon.mod.common.client.render.block.GildedChestBlockRenderer
import com.cobblemon.mod.common.client.render.block.HealingMachineRenderer
import com.cobblemon.mod.common.client.render.block.*
import com.cobblemon.mod.common.client.render.block.TMBlockRenderer
import com.cobblemon.mod.common.client.render.boat.CobblemonBoatRenderer
import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
import com.cobblemon.mod.common.client.render.layer.PokemonOnShoulderRenderer
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BerryModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.BlockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokeBallModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.PokemonModelRepository
import com.cobblemon.mod.common.client.render.pokeball.PokeBallRenderer
@@ -42,13 +50,19 @@ import com.cobblemon.mod.common.client.storage.ClientStorageManager
import com.cobblemon.mod.common.client.trade.ClientTrade
import com.cobblemon.mod.common.data.CobblemonDataProvider
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.gui.CobblemonScreenHandlers
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.item.TechnicalMachineItem
import com.cobblemon.mod.common.client.render.models.blockbench.repository.FossilModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.GenericBedrockEntityModelRepository
import com.cobblemon.mod.common.client.render.models.blockbench.repository.MiscModelRepository
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asTranslated
import net.minecraft.client.color.block.BlockColorProvider
import net.minecraft.client.color.item.ItemColorProvider
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.screen.ingame.HandledScreens
import net.minecraft.client.render.RenderLayer
import net.minecraft.client.render.block.entity.HangingSignBlockEntityRenderer
import net.minecraft.client.render.block.entity.SignBlockEntityRenderer
@@ -102,9 +116,10 @@ object CobblemonClient {
this.registerBlockEntityRenderers()
registerBlockRenderTypes()
//registerColors()
registerColors()
registerFlywheelRenderers()
this.registerEntityRenderers()
registerHandledScreens()
Berries.observable.subscribe {
BerryModelRepository.patchModels()
}
@@ -115,7 +130,6 @@ object CobblemonClient {
PlatformEvents.CLIENT_ITEM_TOOLTIP.subscribe { event ->
val stack = event.stack
val lines = event.lines
val size = lines.size
@Suppress("DEPRECATION")
if (stack.item.registryEntry.key.isPresent && stack.item.registryEntry.key.get().value.namespace == Cobblemon.MODID) {
if (stack.nbt?.getBoolean(DataKeys.HIDE_TOOLTIP) == true) {
@@ -123,14 +137,14 @@ object CobblemonClient {
}
val language = Language.getInstance()
val key = this.baseLangKeyForItem(stack)
val offset = if (size > 1) 1 else 0
val offset = if (lines.size > 1) 1 else 0
if (language.hasTranslation(key)) {
lines.add(size - offset, key.asTranslated().gray())
lines.add(lines.size - offset, key.asTranslated().gray())
}
var i = 1
var listKey = "${key}_$i"
while(language.hasTranslation(listKey)) {
lines.add(size - offset, listKey.asTranslated().gray())
lines.add(lines.size - offset, listKey.asTranslated().gray())
listKey = "${key}_${++i}"
}
}
@@ -145,23 +159,27 @@ object CobblemonClient {
// .apply()
}
/*
fun registerColors() {
this.implementation.registerBlockColors(BlockColorProvider { _, _, _, _ ->
return@BlockColorProvider 0xE0A33A
}, CobblemonBlocks.APRICORN_LEAVES)
this.implementation.registerItemColors(ItemColorProvider { _, _ ->
this.implementation.registerItemColors(ItemColorProvider { stack, tint ->
return@ItemColorProvider 0xE0A33A
}, CobblemonItems.APRICORN_LEAVES)
}, CobblemonItems.TECHNICAL_MACHINE)
this.implementation.registerItemColors(TechnicalMachineItem::getItemColor, CobblemonItems.TECHNICAL_MACHINE)
}
*/
private fun registerBlockRenderTypes() {
this.implementation.registerBlockRenderType(RenderLayer.getCutoutMipped(), CobblemonBlocks.APRICORN_LEAVES)
this.implementation.registerBlockRenderType(RenderLayer.getTranslucent(), CobblemonBlocks.TM_MACHINE)
this.implementation.registerBlockRenderType(
RenderLayer.getCutout(),
CobblemonBlocks.GILDED_CHEST,
CobblemonBlocks.FOSSIL_ANALYZER,
CobblemonBlocks.APRICORN_DOOR,
CobblemonBlocks.APRICORN_TRAPDOOR,
@@ -215,6 +233,8 @@ object CobblemonClient {
CobblemonBlocks.MEDIUM_BUDDING_SKY_TUMBLESTONE,
CobblemonBlocks.LARGE_BUDDING_SKY_TUMBLESTONE,
CobblemonBlocks.SKY_TUMBLESTONE_CLUSTER,
CobblemonBlocks.GIMMIGHOUL_CHEST,
CobblemonBlocks.DISPLAY_CASE
)
this.createBoatModelLayers()
@@ -244,6 +264,9 @@ object CobblemonClient {
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.HANGING_SIGN, ::HangingSignBlockEntityRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.FOSSIL_ANALYZER, ::FossilAnalyzerRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.RESTORATION_TANK, ::RestorationTankRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.TM_BLOCK, ::TMBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.GILDED_CHEST, ::GildedChestBlockRenderer)
this.implementation.registerBlockEntityRenderer(CobblemonBlockEntities.DISPLAY_CASE, ::DisplayCaseRenderer)
}
private fun registerEntityRenderers() {
@@ -255,6 +278,8 @@ object CobblemonClient {
this.implementation.registerEntityRenderer(CobblemonEntities.BOAT) { ctx -> CobblemonBoatRenderer(ctx, false) }
LOGGER.info("Registering Boat with Chest renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.CHEST_BOAT) { ctx -> CobblemonBoatRenderer(ctx, true) }
LOGGER.info("Registering Generic Bedrock Entity renderer")
this.implementation.registerEntityRenderer(CobblemonEntities.GENERIC_BEDROCK_ENTITY, ::GenericBedrockRenderer)
}
fun reloadCodedAssets(resourceManager: ResourceManager) {
@@ -262,12 +287,14 @@ object CobblemonClient {
BedrockParticleEffectRepository.loadEffects(resourceManager)
BedrockAnimationRepository.loadAnimations(
resourceManager = resourceManager,
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories
directories = PokemonModelRepository.animationDirectories + PokeBallModelRepository.animationDirectories + FossilModelRepository.animationDirectories + BlockEntityModelRepository.animationDirectories + GenericBedrockEntityModelRepository.animationDirectories
)
PokemonModelRepository.reload(resourceManager)
PokeBallModelRepository.reload(resourceManager)
BerryModelRepository.reload(resourceManager)
FossilModelRepository.reload(resourceManager)
BlockEntityModelRepository.reload(resourceManager)
GenericBedrockEntityModelRepository.reload(resourceManager)
MiscModelRepository.reload(resourceManager)
LOGGER.info("Loaded assets")
}
@@ -293,4 +320,15 @@ object CobblemonClient {
}
}
private fun registerHandledScreens() {
HandledScreens.register(
CobblemonScreenHandlers.TMM_SCREEN,
::TMMHandledScreen
)
HandledScreens.register(
CobblemonScreenHandlers.MOD_STATION_SCREEN,
::ModStationHandledScreen
)
}
}
@@ -0,0 +1,48 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.util.math.MatrixStack
class TypeReturnIcon(
val x: Number,
val y: Number,
val centeredX: Boolean = false,
val small: Boolean = false,
val secondaryOffset: Float = 15F,
val doubleCenteredOffset: Float = 7.5F,
val opacity: Float = 1F
) {
companion object {
private const val TYPE_ICON_DIAMETER = 36
private const val SCALE = 0.5F
private val typeReturnResource = cobblemonResource("textures/gui/type_return.png")
private val smallTypeReturnResource = cobblemonResource("textures/gui/type_return_small.png")
}
fun render(context: DrawContext) {
val diameter = if (small) (TYPE_ICON_DIAMETER / 2) else TYPE_ICON_DIAMETER
blitk(
matrixStack = context.matrices,
texture = if (small) smallTypeReturnResource else typeReturnResource,
x = (x.toFloat()) / SCALE - 9,
y = y.toFloat() / SCALE,
height = diameter,
width = diameter,
alpha = opacity,
scale = SCALE
)
}
}
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui.battle.subscreen
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
import com.cobblemon.mod.common.api.text.yellow
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.SingleActionRequest
import com.cobblemon.mod.common.client.gui.battle.BattleGUI
@@ -21,7 +23,6 @@ import net.minecraft.client.gui.Selectable
import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.MutableText
import net.minecraft.util.Identifier
class BattleGeneralActionSelection(
@@ -61,9 +62,8 @@ class BattleGeneralActionSelection(
}
addOption(rank++, battleLang("ui.run"), BattleGUI.runResource) {
CobblemonClient.battle?.minimised = true
MinecraftClient.getInstance().player?.sendMessage(battleLang("run_prompt"), false)
playDownSound(MinecraftClient.getInstance().soundManager)
MinecraftClient.getInstance().player?.sendMessage(battleLang("flee"))
}
}
}
@@ -0,0 +1,28 @@
package com.cobblemon.mod.common.client.gui.modstation
import com.cobblemon.mod.common.gui.ModStationScreenHandler
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.ScreenHandlerListener
import net.minecraft.text.Text
class ModStationHandledScreen(
val handler: ModStationScreenHandler,
val inventory: PlayerInventory,
title: Text?
) : HandledScreen<ModStationScreenHandler>(handler, inventory, title), ScreenHandlerListener {
override fun drawBackground(context: DrawContext?, delta: Float, mouseX: Int, mouseY: Int) {
}
override fun onSlotUpdate(handler: ScreenHandler?, slotId: Int, stack: ItemStack?) {
}
override fun onPropertyUpdate(handler: ScreenHandler?, property: Int, value: Int) {
}
}
@@ -46,7 +46,7 @@ import net.minecraft.text.Text
* @author Qu
* @since 2022-06-18
*/
class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter.title".asTranslated()) {
class StarterSelectionScreen(private val categories: List<RenderableStarterCategory>): Screen("cobblemon.ui.starter.title".asTranslated()) {
companion object {
// Size of UI at scale 1
@@ -63,22 +63,14 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
private val doubleTypeBackground = cobblemonResource("textures/gui/starterselection/starterselection_type_slot2.png")
}
lateinit var categories: List<RenderableStarterCategory>
lateinit var currentCategory: RenderableStarterCategory
lateinit var modelWidget: ModelWidget
lateinit var currentPokemon: RenderablePokemon
var currentSelection = 0
lateinit var rightButton: ArrowButton
lateinit var leftButton: ArrowButton
lateinit var typeWidget: TypeWidget
lateinit var selectionButton: SelectionButton
lateinit var starterRoundaboutCenter: StarterRoundabout
lateinit var starterRoundaboutLeft: StarterRoundabout
lateinit var starterRoundaboutRight: StarterRoundabout
constructor(categories: List<RenderableStarterCategory>) : this() {
this.categories = categories
}
private var currentSelection = 0
private lateinit var currentCategory: RenderableStarterCategory
private lateinit var modelWidget: ModelWidget
private lateinit var currentPokemon: RenderablePokemon
private lateinit var typeWidget: TypeWidget
private lateinit var starterRoundaboutCenter: StarterRoundabout
private lateinit var starterRoundaboutLeft: StarterRoundabout
private lateinit var starterRoundaboutRight: StarterRoundabout
override fun init() {
super.init()
@@ -102,7 +94,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
)
)
rightButton = ArrowButton(
val rightButton = ArrowButton(
pX = x + 183, pY = y + 151,
pWidth = 9, pHeight = 14,
right = true
@@ -110,7 +102,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
right()
}
leftButton = ArrowButton(
val leftButton = ArrowButton(
pX = x + 72, pY = y + 151,
pWidth = 9, pHeight = 14,
right = false
@@ -137,7 +129,7 @@ class StarterSelectionScreen private constructor(): Screen("cobblemon.ui.starter
addDrawableChild(modelWidget)
selectionButton = SelectionButton(
val selectionButton = SelectionButton(
pX = x + 106, pY = y + 124,
pWidth = SelectionButton.BUTTON_WIDTH, pHeight = SelectionButton.BUTTON_HEIGHT
) {
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.client.gui.summary
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Move
@@ -59,7 +60,7 @@ import net.minecraft.text.Text
*
* @param selection The index the [party] will have as the base [selectedPokemon].
*/
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, selection: Int): Screen(Text.translatable("cobblemon.ui.summary.title")), Schedulable {
class Summary private constructor(party: Collection<Pokemon?>, private val editable: Boolean, private val selection: Int): Screen(Text.translatable("cobblemon.ui.summary.title")), Schedulable {
companion object {
const val BASE_WIDTH = 331
@@ -106,24 +107,27 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
override val schedulingTracker = SchedulingTracker()
internal var selectedPokemon: Pokemon
internal lateinit var selectedPokemon: Pokemon
private lateinit var mainScreen: ClickableWidget
lateinit var sideScreen: Element
lateinit var modelWidget: ModelWidget
lateinit var nicknameEntryWidget: NicknameEntryWidget
private lateinit var modelWidget: ModelWidget
private lateinit var nicknameEntryWidget: NicknameEntryWidget
private val summaryTabs = mutableListOf<SummaryTab>()
private var mainScreenIndex = INFO
var sideScreenIndex = PARTY
private val party = ArrayList(party)
init {
/**
* Initializes the Summary Screen
*/
override fun init() {
super.init()
if (this.party.isEmpty()) {
throw IllegalArgumentException("Summary UI cannot display zero Pokemon")
}
if (this.party.size > 6) {
throw IllegalArgumentException("Summary UI cannot display more than six Pokemon")
}
val idealSelected = this.party[selection]
if (idealSelected == null) {
this.selectedPokemon = this.party.first { it != null }!!
@@ -131,14 +135,6 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
this.selectedPokemon = idealSelected
}
this.listenToMoveSet()
}
/**
* Initializes the Summary Screen
*/
public override fun init() {
clearChildren()
super.init()
val x = (width - BASE_WIDTH) / 2
val y = (height - BASE_HEIGHT) / 2
@@ -245,6 +241,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
rotationY = 325F,
offsetY = -10.0
)
addDrawable(this.modelWidget)
}
fun swapPartySlot(sourceIndex: Int, targetIndex: Int) {
@@ -281,8 +278,12 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
listenToMoveSet()
displayMainScreen(mainScreenIndex)
children().find { it is EvolutionSelectScreen }?.let(this::remove)
modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
if (this::modelWidget.isInitialized) {
this.modelWidget.pokemon = selectedPokemon.asRenderablePokemon()
}
if (this::nicknameEntryWidget.isInitialized) {
this.nicknameEntryWidget.setSelectedPokemon(selectedPokemon)
}
}
private var moveSetSubscription: ObservableSubscription<MoveSet>? = null
@@ -309,7 +310,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
*/
private fun displayMainScreen(screen: Int) {
// Get stat tab index if currently displaying stat screen
var subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
val subIndex = if (mainScreenIndex == STATS && mainScreen is StatWidget) (mainScreen as StatWidget).statTabIndex else 0
mainScreenIndex = screen
if (::mainScreen.isInitialized) remove(mainScreen)
@@ -408,10 +409,10 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
}
}
override fun render(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
renderBackground(context)
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
//this.renderBackground(context)
schedulingTracker.update(pPartialTicks / 20F)
schedulingTracker.update(delta / 20F)
val x = (width - BASE_WIDTH) / 2
val y = (height - BASE_HEIGHT) / 2
@@ -427,7 +428,7 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
height = PORTRAIT_SIZE
)
modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
//modelWidget.render(context, pMouseX, pMouseY, pPartialTicks)
// Render Base Resource
blitk(
@@ -576,29 +577,29 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
)
// Render all added Widgets
super.render(context, pMouseX, pMouseY, pPartialTicks)
super.render(context, mouseX, mouseY, delta)
// Render Item Tooltip
if (!heldItem.isEmpty) {
val itemHovered = pMouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && pMouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.drawItemTooltip(MinecraftClient.getInstance().textRenderer, heldItem, pMouseX, pMouseY)
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.drawItemTooltip(MinecraftClient.getInstance().textRenderer, heldItem, mouseX, mouseY)
}
}
/**
* Whether this Screen should pause the Game in SinglePlayer
*/
override fun shouldPause(): Boolean {
return false
}
override fun shouldPause(): Boolean = false
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
return children().any { it.mouseScrolled(mouseX, mouseY, amount) }
}
/*
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
return children().any { it.mouseClicked(mouseX, mouseY, button) }
}
*/
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
if (sideScreenIndex == MOVE_SWAP || sideScreenIndex == EVOLVE) sideScreen.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
@@ -606,17 +607,17 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if ((keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_KP_ENTER) && nicknameEntryWidget.isFocused) { // Enter pressed
nicknameEntryWidget.isFocused = false
if ((keyCode == InputUtil.GLFW_KEY_ENTER || keyCode == InputUtil.GLFW_KEY_KP_ENTER)
&& this::nicknameEntryWidget.isInitialized
&& this.nicknameEntryWidget.isFocused
) {
this.focused = null
}
return nicknameEntryWidget.keyPressed(keyCode, scanCode, modifiers) || super.keyPressed(keyCode, scanCode, modifiers)
}
override fun charTyped(chr: Char, modifiers: Int): Boolean {
return nicknameEntryWidget.charTyped(chr, modifiers) || super.charTyped(chr, modifiers)
return super.keyPressed(keyCode, scanCode, modifiers)
}
fun playSound(soundEvent: SoundEvent) {
MinecraftClient.getInstance().soundManager.play(PositionedSoundInstance.master(soundEvent, 1.0F))
}
}
@@ -19,6 +19,7 @@ import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.text.MutableText
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class SummaryButton(
@@ -27,7 +28,7 @@ class SummaryButton(
val buttonWidth: Number,
val buttonHeight: Number,
val clickAction: PressAction,
private val text: MutableText? = null,
private val text: MutableText = Text.empty(),
private val resource: Identifier,
private val activeResource: Identifier? = null,
private val renderRequirement: ((button: SummaryButton) -> Boolean) = { true },
@@ -68,18 +69,16 @@ class SummaryButton(
)
// Render Text
if (text != null) {
drawScaledText(
context = context,
font = if (largeText) CobblemonResources.DEFAULT_LARGE else null,
text = if (boldText) text.bold() else text,
x = buttonX + (buttonWidth.toFloat() / 2),
y = buttonY + (buttonHeight.toFloat() / 2) - ((TEXT_HEIGHT / 2) * textScale),
scale = textScale,
centered = true,
shadow = true
)
}
drawScaledText(
context = context,
font = if (largeText) CobblemonResources.DEFAULT_LARGE else null,
text = if (boldText) text.bold() else text,
x = buttonX + (buttonWidth.toFloat() / 2),
y = buttonY + (buttonHeight.toFloat() / 2) - ((TEXT_HEIGHT / 2) * textScale),
scale = textScale,
centered = true,
shadow = true
)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
@@ -0,0 +1,110 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.featurerenderers
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeature
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats.StatWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.gui.DrawContext
import net.minecraft.text.MutableText
import net.minecraft.util.Identifier
import net.minecraft.util.math.MathHelper
import net.minecraft.util.math.Vec3d
/**
* Renders an [IntSpeciesFeature] as a bar in the summary screen.
*
* @author Hiroku
* @since November 13th, 2023
*/
class BarSummarySpeciesFeatureRenderer(
override val name: String,
val displayName: MutableText,
val min: Int,
val max: Int,
val colour: Vec3d,
val underlay: Identifier,
val overlay: Identifier,
val pokemon: Pokemon
) : SummarySpeciesFeatureRenderer<IntSpeciesFeature> {
override fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon, feature: IntSpeciesFeature) {
val value = feature.value
val barRatio = (value - min) / (max - min).toFloat()
val barWidth = MathHelper.ceil(barRatio * 108)
blitk(
matrixStack = drawContext.matrices,
texture = underlay,
x = x,
y = y,
height = 28,
width = 124
)
val red = colour.x / 255F
val green = colour.y / 255F
val blue = colour.z / 255F
blitk(
matrixStack = drawContext.matrices,
texture = CobblemonResources.WHITE,
x = x + 8,
y = y + 16,
height = 10,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = drawContext.matrices,
texture = overlay,
x = x / StatWidget.SCALE,
y = (y + 16) / StatWidget.SCALE,
height = 20,
width = 248,
scale = StatWidget.SCALE
)
// Label
drawScaledText(
context = drawContext,
font = CobblemonResources.DEFAULT_LARGE,
text = displayName.bold(),
x = x + 62,
y = y + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = drawContext,
text = value.toString().text(),
x = x + 11,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
drawScaledText(
context = drawContext,
text = "${MathHelper.floor(barRatio * 100)}%".text(),
x = x + 113,
y = y + 6,
scale = StatWidget.SCALE,
centered = true
)
}
}
@@ -0,0 +1,32 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.summary.featurerenderers
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeature
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.client.gui.DrawContext
/**
* A renderer for some kind of [SynchronizedSpeciesFeature] so that it will display
* in the summary screen of the Pokémon (under other stats).
*
* @author Hiroku
* @since November 13th, 2023
*/
interface SummarySpeciesFeatureRenderer<T : SynchronizedSpeciesFeature> {
/** The name of the feature (so we know where to look to find it in the [Pokemon]) */
val name: String
/** Draws it at a particular position. */
fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon, feature: T)
fun render(drawContext: DrawContext, x: Float, y: Float, pokemon: Pokemon): Boolean {
val feature = pokemon.getFeature<T>(name) ?: return false
render(drawContext, x, y, pokemon, feature)
return true
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.client.gui.summary.widgets
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.summary.Summary
@@ -23,15 +22,14 @@ import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import net.minecraft.util.Identifier
import org.joml.Quaternionf
import org.joml.Vector3f
class PartySlotWidget(
private val pX: Number,
private val pY: Number,
pX: Number,
pY: Number,
private val partyWidget: PartyWidget,
private val summary: Summary,
private val pokemon: Pokemon?,
@@ -26,9 +26,7 @@ class InfoBlockWidget(
private val font: Identifier,
) : SoundlessWidget(pX, pY, blockWidth, blockHeight, Text.literal("InfoBlockWidget")) {
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
drawScaledText(
context = context,
font = font,
@@ -15,14 +15,13 @@ import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.PositionedSoundInstance
import net.minecraft.client.sound.SoundManager
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
class ReorderMoveButton(
val pX: Int, val pY: Int,
private val isUp: Boolean,
onPress: PressAction
): ButtonWidget((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress, DEFAULT_NARRATION_SUPPLIER) {
): ButtonWidget((pX - OFFSET_X).toInt(), (pY + (if (isUp) OFFSET_Y_UP else OFFSET_Y_DOWN)), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.empty(), onPress, DEFAULT_NARRATION_SUPPLIER) {
companion object {
private const val WIDTH = 8
@@ -29,7 +29,7 @@ class SwapMoveButton(
var move: MoveTemplate,
var movesWidget: MovesWidget,
onPress: PressAction
): ButtonWidget((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.literal(""), onPress, DEFAULT_NARRATION_SUPPLIER) {
): ButtonWidget((pX + OFFSET_X).toInt(), (pY + OFFSET_Y).toInt(), (WIDTH * SCALE).toInt(), (HEIGHT * SCALE).toInt(), Text.empty(), onPress, DEFAULT_NARRATION_SUPPLIER) {
override fun mouseDragged(d: Double, e: Double, i: Int, f: Double, g: Double): Boolean {
return false
@@ -10,6 +10,8 @@ package com.cobblemon.mod.common.client.gui.summary.widgets.screens.stats
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.feature.SynchronizedSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.text.bold
@@ -54,7 +56,7 @@ class StatWidget(
private const val WIDTH = 134
private const val HEIGHT = 148
private const val SCALE = 0.5F
const val SCALE = 0.5F
private const val WHITE = 0x00FFFFFF
private const val GREY = 0x00AAAAAA
@@ -64,6 +66,7 @@ class StatWidget(
private val statsBaseResource = cobblemonResource("textures/gui/summary/summary_stats_chart_base.png")
private val statsChartResource = cobblemonResource("textures/gui/summary/summary_stats_chart.png")
private val statsOtherBaseResource = cobblemonResource("textures/gui/summary/summary_stats_other_base.png")
private val statsOtherBarTemplate = cobblemonResource("textures/gui/summary/summary_stats_other_bar.png")
private val friendshipOverlayResource = cobblemonResource("textures/gui/summary/summary_stats_friendship_overlay.png")
private val tabMarkerResource = cobblemonResource("textures/gui/summary/summary_stats_tab_marker.png")
private val statIncreaseResource = cobblemonResource("textures/gui/summary/summary_stats_icon_increase.png")
@@ -84,6 +87,10 @@ class StatWidget(
}
var statTabIndex = tabIndex
val renderableFeatures = SpeciesFeatures
.getFeaturesFor(pokemon.species)
.filterIsInstance<SynchronizedSpeciesFeatureProvider<*>>()
.mapNotNull { it.getRenderer(pokemon) }
private fun drawTriangle(
colour: Vector3f,
@@ -178,6 +185,74 @@ class StatWidget(
// drawTriangle(colour, specialAttackPoint, centerPoint, hpPoint)
}
private fun drawFriendship(moduleX: Int, moduleY: Int, matrices: MatrixStack, context: DrawContext, friendship: Int) {
val barRatio = friendship / 255F
val barWidth = ceil(barRatio * 108)
blitk(
matrixStack = matrices,
texture = statsOtherBarTemplate,
x = moduleX,
y = moduleY,
height = 28,
width = 124
)
val red = 1
val green: Number = if (pokemon.friendship >= 160) 0.28 else 0.56
val blue: Number = if (pokemon.friendship >= 160) 0.4 else 0.64
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = moduleX + 8,
y = moduleY + 18,
height = 8,
width = barWidth,
red = red,
green = green,
blue = blue
)
blitk(
matrixStack = matrices,
texture = friendshipOverlayResource,
x = moduleX / SCALE,
y = (moduleY + 16) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
// Label
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.stats.friendship").bold(),
x = moduleX + 62,
y = moduleY + 2.5,
centered = true,
shadow = true
)
drawScaledText(
context = context,
text = friendship.toString().text(),
x = moduleX + 11,
y = moduleY + 6,
scale = SCALE,
centered = true
)
drawScaledText(
context = context,
text = "${floor(barRatio * 100)}%".text(),
x = moduleX + 113,
y = moduleY + 6,
scale = SCALE,
centered = true
)
}
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
val renderChart = statTabIndex != OTHER
@@ -328,60 +403,28 @@ class StatWidget(
renderModifiedStatIcon(matrices, nature.decreasedStat, false)
}
} else {
// Friendship
val friendshipBarWidthMax = 108
val friendshipRatio = pokemon.friendship / 255F
val friendshipBarWidth = ceil(friendshipRatio * friendshipBarWidthMax)
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = x + 13,
y = y + 27,
height = 8,
width = friendshipBarWidth,
red = 0.92,
green = if (pokemon.friendship >= 160) 0.28 else 0.7,
blue = if (pokemon.friendship >= 160) 0.4 else 0.28
)
var drawY = y + 11
blitk(
matrixStack = matrices,
texture = friendshipOverlayResource,
x = (x + 5) / SCALE,
y = (y + 26) / SCALE,
height = 20,
width = 248,
scale = SCALE
)
drawFriendship(x + 5, drawY, matrices, context, pokemon.friendship)
drawY += 30
// Label
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.stats.friendship").bold(),
x = x + 67,
y = y + 12.5,
centered = true,
shadow = true
)
for (renderableFeature in renderableFeatures) {
val rendered = renderableFeature.render(
drawContext = context,
x = x + 5F,
y = drawY.toFloat(),
pokemon = pokemon
)
drawScaledText(
context = context,
text = pokemon.friendship.toString().text(),
x = x + 16,
y = y + 16,
scale = SCALE,
centered = true
)
if (rendered) {
drawY += 30
}
}
// for (value in summaries) {
// drawBarModule(x + 5, drawY, matrices, context, value)
// }
drawScaledText(
context = context,
text = "${floor(friendshipRatio * 100)}%".text(),
x = x + 118,
y = y + 16,
scale = SCALE,
centered = true
)
}
}
@@ -0,0 +1,37 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.text.Text
class DiscButton(
pX: Int, pY: Int,
onPress: PressAction,
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Text.literal("All Types"), onPress, DEFAULT_NARRATION_SUPPLIER) {
companion object {
private const val WIDTH = 53F
private const val HEIGHT = 53F
val DISC_BUTTON = cobblemonResource("textures/gui/tm/disc_button.png")
}
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
blitk(
matrixStack = context.matrices,
texture = DISC_BUTTON,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
}
override fun playDownSound(soundManager: SoundManager) {}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
@@ -0,0 +1,54 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.text.Text
class EjectButton(
pX: Int, pY: Int,
val small: Boolean = false,
onPress: PressAction
): ButtonWidget(
pX,
pY,
if (small) SMALL_WIDTH.toInt() else LARGE_WIDTH.toInt(),
if (small) SMALL_HEIGHT.toInt() else LARGE_HEIGHT.toInt(),
Text.literal("Eject"),
onPress,
DEFAULT_NARRATION_SUPPLIER
) {
companion object {
private const val LARGE_WIDTH = 54F
private const val LARGE_HEIGHT = 14F
private const val SMALL_WIDTH = 14F
private const val SMALL_HEIGHT = 14F
val SMALL_BUTTON = cobblemonResource("textures/gui/tm/eject_button_small.png")
val LARGE_BUTTON = cobblemonResource("textures/gui/tm/eject_button_large.png")
}
val height = if (small) SMALL_HEIGHT else LARGE_HEIGHT
val width = if (small) SMALL_WIDTH else LARGE_WIDTH
val texture = if (small) SMALL_BUTTON else LARGE_BUTTON
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
blitk(
matrixStack = context.matrices,
texture = texture,
x = x,
y = y,
width = width,
height = height,
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) height else 0,
textureHeight = height * 2
)
}
override fun playDownSound(soundManager: SoundManager) {}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + width)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + height))
}
@@ -0,0 +1,59 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundManager
import net.minecraft.text.Text
class TMListingButton(
pX: Int, pY: Int,
onPress: PressAction,
val tm: TechnicalMachine
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), Text.literal("All Types"), onPress, DEFAULT_NARRATION_SUPPLIER) {
companion object {
private const val HEIGHT = 20F
private const val WIDTH = 140F
val TM_LISTING_BUTTON = cobblemonResource("textures/gui/tm/tm_selection_listing.png")
}
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
blitk(
matrixStack = context.matrices,
texture = TM_LISTING_BUTTON,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
val item = CobblemonItems.TECHNICAL_MACHINE
renderScaledGuiItemIcon(
itemStack = item.setNbt(item.defaultStack, tm.id().toString()),
x = x.toDouble() + 2,
y = y.toDouble() + 2
)
drawScaledText(
context = context,
text = tm.translatedMoveName(),
x = x + 25,
y = y + 7.5,
scale = 0.75f
)
}
override fun playDownSound(soundManager: SoundManager) {}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
@@ -0,0 +1,611 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonNetwork.sendPacketToServer
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.ColourLibrary
import com.cobblemon.mod.common.api.gui.MultiLineLabelK
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.moves.Moves
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import com.cobblemon.mod.common.api.tms.TechnicalMachineRecipe
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.block.TMBlock
import com.cobblemon.mod.common.block.entity.TMBlockEntity
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.client.gui.MoveCategoryIcon
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
import com.cobblemon.mod.common.gui.TMMScreenHandler
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.PokemonState
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.Drawable
import net.minecraft.client.gui.Element
import net.minecraft.client.gui.Selectable
import net.minecraft.client.gui.screen.ingame.HandledScreen
import net.minecraft.entity.player.PlayerInventory
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.registry.Registries
import net.minecraft.screen.ScreenHandler
import net.minecraft.screen.ScreenHandlerListener
import net.minecraft.text.Text
import net.minecraft.util.Identifier
class TMMHandledScreen(
val handler: TMMScreenHandler,
val inventory: PlayerInventory,
title: Text?
) : HandledScreen<TMMScreenHandler>(handler, inventory, title), ScreenHandlerListener {
companion object {
val TEXTURE_HEIGHT = 222
val TEXTURE_WIDTH = 254
val TYPE_MENU_MODE = 1
val TM_BROWSING_MODE = 2
val TYPE_SELECTION_BASE = cobblemonResource("textures/gui/tm/type_selection_base.png")
val TM_SELECTION_BASE = cobblemonResource("textures/gui/tm/tm_selection_base.png")
val TM_SELECTION_BORDER = cobblemonResource("textures/gui/tm/tm_selection_border.png")
val TM_SELECTION_LISTING = cobblemonResource("textures/gui/tm/tm_selection_listing.png")
val EJECT_BUTTON_SMALL = cobblemonResource("textures/gui/tm/eject_button_small.png")
val EJECT_BUTTON_LARGE = cobblemonResource("textures/gui/tm/eject_button_large.png")
val PARTY_SLOT = cobblemonResource("textures/gui/tm/party_slot.png")
val TEACH_BUTTON = cobblemonResource("textures/gui/tm/teach_button.png")
}
// val selectedTM: TechnicalMachine? = TechnicalMachines.tmMap[cobblemonResource("thunderbolt")]!!
var selectedTM: TechnicalMachine? = null
var tmList: SettableObservable<MutableList<TechnicalMachine>> = SettableObservable(mutableListOf())
var output: ItemStack? = null
val children: MutableMap<String, Element> = mutableMapOf()
var mode: Int = TYPE_MENU_MODE
var sortType: ElementalType? = null
var selectedPokemon: Pokemon? = null
var scroll: TMScrollingList? = null
init {
scroll = TMScrollingList(
x = x + 132,
y = y + 45,
parent = this
)
}
// val selectedTM = TechnicalMachine("stompingtantrum", null, emptyList(), "ground", null, null)
/**
* Used instead of `addDrawableChild()` to avoid re-drawing children.
*/
private fun <T> addChild(drawableElement: T, identifier: String) where T : Element, T : Drawable, T : Selectable {
if (children.contains(identifier)) return
addDrawableChild(drawableElement)
children[identifier] = drawableElement
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (scroll?.isHovered(mouseX, mouseY) == true) {
scroll!!.mouseClicked(mouseX, mouseY, button)
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double): Boolean {
if (scroll?.isHovered(mouseX, mouseY) == true) {
scroll!!.mouseScrolled(mouseX, mouseY, amount)
return true
}
return false
}
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
if (mode != TYPE_MENU_MODE) scroll?.mouseDragged(
mouseX,
mouseY,
button,
deltaX,
deltaY
)
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
fun drawBlankCraftingRecipe(context: DrawContext) {
val x = (width - TEXTURE_WIDTH) / 2
val y = (height - TEXTURE_HEIGHT) / 2
// ingredient
renderScaledGuiItemIcon(
itemStack = Items.AMETHYST_SHARD.defaultStack,
x = (x + 197 + 36).toDouble(),
y = (y + 127).toDouble(),
zTranslation = 1f
)
drawScaledText(
context = context,
text = lang("ui.tms.material_cost"),
x = (x + 223),
y = (y + 117.5),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = Text.translatable(CobblemonItems.BLANK_TM.translationKey).bold(),
x = (x + 223),
y = (y + 12),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.power"),
x = (x + 199),
y = (y + 25),
scale = 0.5f
)
drawScaledText(
context = context,
text = Text.literal("-"),
x = (x + 241),
y = (y + 25),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.accuracy_short"),
x = (x + 199),
y = (y + 35),
scale = 0.5f
)
drawScaledText(
context = context,
text = Text.literal("-"),
x = (x + 241),
y = (y + 35),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.pp"),
x = (x + 199),
y = (y + 45),
scale = 0.5f
)
drawScaledText(
context = context,
text = Text.literal("-"),
x = (x + 241),
y = (y + 45),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.summary.title"),
x = (x + 199),
y = (y + 54.5),
scale = 0.5f
)
val scale = 0.5f
context.matrices.push()
context.matrices.scale(scale, scale, 1f)
MultiLineLabelK.create(
component = lang("ui.tms.blank_tm_desc"),
width = 49 / scale,
maxLines = 7
).renderLeftAligned(
context = context,
x = (x + 199) / scale,
y = (y + 62) / scale,
ySpacing = 5.5 / scale,
colour = ColourLibrary.WHITE,
shadow = true
)
context.matrices.pop()
}
fun drawMoveInfo(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
val currentTm = selectedTM
if (currentTm == null) {
return drawBlankCraftingRecipe(context)
}
val x = (width - TEXTURE_WIDTH) / 2
val y = (height - TEXTURE_HEIGHT) / 2
val move = Moves.getByNameOrDummy(currentTm.moveName)
// blank tm
renderScaledGuiItemIcon(
itemStack = CobblemonItems.BLANK_TM.defaultStack,
x = (x + 197).toDouble(),
y = (y + 127).toDouble(),
)
val typeGem = Registries.ITEM.get(ElementalTypes.get(currentTm.type)?.typeGem).defaultStack
// type gem
renderScaledGuiItemIcon(
itemStack = typeGem ?: ItemStack.EMPTY,
x = (x + 197 + 18).toDouble(),
y = (y + 127).toDouble(),
)
val recipe = currentTm.recipe ?: TechnicalMachineRecipe(Identifier.of("minecraft", "air")!!, 1)
// ingredient
renderScaledGuiItemIcon(
itemStack = Registries.ITEM.get(recipe.item).defaultStack,
x = (x + 197 + 36).toDouble(),
y = (y + 127).toDouble(),
zTranslation = 1f
)
context.matrices.push()
context.matrices.translate(0f, 0f, 2f)
if (recipe.count > 1) {
val xIncrease = if (recipe.count < 10) 47 else 41
drawScaledText(
context = context,
text = Text.literal(recipe.count.toString()),
x = (x + 197 + xIncrease).toDouble(),
y = (y + 136).toDouble(),
shadow = true
)
}
context.matrices.pop()
drawScaledText(
context = context,
text = lang("ui.tms.material_cost"),
x = (x + 223),
y = (y + 117.5),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = currentTm.translatedMoveName().bold(),
x = (x + 223),
y = (y + 12),
scale = 0.5f,
centered = true
)
MoveCategoryIcon(
x = (x + 237.5),
y = (y + 52.5),
category = Moves.getByNameOrDummy(currentTm.moveName).damageCategory
).render(context)
drawScaledText(
context = context,
text = lang("ui.power"),
x = (x + 199),
y = (y + 25),
scale = 0.5f
)
drawScaledText(
context = context,
text = Text.literal(move.power.toInt().toString()),
x = (x + 241),
y = (y + 25),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.accuracy_short"),
x = (x + 199),
y = (y + 35),
scale = 0.5f
)
drawScaledText(
context = context,
text = Text.literal(move.accuracy.toInt().toString() + "%"),
x = (x + 241),
y = (y + 35),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.pp"),
x = (x + 199),
y = (y + 45),
scale = 0.5f
)
drawScaledText(
context = context,
text = Text.literal(move.pp.toString()),
x = (x + 241),
y = (y + 45),
scale = 0.5f,
centered = true
)
drawScaledText(
context = context,
text = lang("ui.summary.title"),
x = (x + 199),
y = (y + 54.5),
scale = 0.5f
)
val scale = 0.5f
context.matrices.push()
context.matrices.scale(scale, scale, 1f)
MultiLineLabelK.create(
component = lang("move.${currentTm.moveName}.desc"),
width = 49 / scale,
maxLines = 7
).renderLeftAligned(
context = context,
x = (x + 199) / scale,
y = (y + 62) / scale,
ySpacing = 5.5 / scale,
colour = ColourLibrary.WHITE,
shadow = true
)
context.matrices.pop()
}
fun drawTypeMenu(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
val x = (width - TEXTURE_WIDTH) / 2
val y = (height - TEXTURE_HEIGHT) / 2
blitk(
matrixStack = context.matrices,
texture = TYPE_SELECTION_BASE,
x = x, // horizontal placement of GUI
y = y, // vertical placement of GUI
width = TEXTURE_WIDTH, // scale of the GUI width
height = TEXTURE_HEIGHT // scale of the GUI height
)
fun drawTypeRow(drawX: Int, drawY: Int, types: List<ElementalType>) {
var xOffset = 0
for (type in types) {
addChild(TypeButton(
pX = drawX + 32 + xOffset,
pY = drawY + 16,
type = type,
onPress = {
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
tmList.set(TechnicalMachine.filterTms(null, type, null).toMutableList())
sortType = type
mode = TM_BROWSING_MODE
clearGUI()
scroll?.scrollAmount = 0.0
}
), "${type.name}type")
xOffset += 24
}
}
drawTypeRow(x, y, listOf(
ElementalTypes.NORMAL,
ElementalTypes.FIRE,
ElementalTypes.WATER,
ElementalTypes.GRASS,
ElementalTypes.ELECTRIC,
ElementalTypes.ICE
))
drawTypeRow(x + 12, y + 24, listOf(
ElementalTypes.FIGHTING,
ElementalTypes.POISON,
ElementalTypes.GROUND,
ElementalTypes.FLYING,
))
drawTypeRow(x, y + 48, listOf(
ElementalTypes.PSYCHIC,
ElementalTypes.BUG,
ElementalTypes.ROCK,
ElementalTypes.GHOST,
))
drawTypeRow(x + 12, y + 72, listOf(
ElementalTypes.DRAGON,
ElementalTypes.DARK,
ElementalTypes.STEEL,
ElementalTypes.FAIRY,
))
addChild(
DiscButton(
pX = x + 134,
pY = y + 50,
onPress = {
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
tmList.set(TechnicalMachine.filterTms(null, null, null).toMutableList())
mode = TM_BROWSING_MODE
sortType = null
scroll?.scrollAmount = 0.0
clearGUI()
}
), "disc"
)
}
fun drawTmSelectMenu(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
val x = (width - TEXTURE_WIDTH) / 2
val y = (height - TEXTURE_HEIGHT) / 2
blitk(
matrixStack = context.matrices,
texture = TM_SELECTION_BASE,
x = x, // horizontal placement of GUI
y = y, // vertical placement of GUI
width = TEXTURE_WIDTH, // scale of the GUI width
height = TEXTURE_HEIGHT // scale of the GUI height
)
blitk(
matrixStack = context.matrices,
texture = TM_SELECTION_BORDER,
x = x + 31, // horizontal placement of GUI
y = y + 14, // vertical placement of GUI
width = 157,
height = 101
)
val displayType = sortType
addChild(
TypeButton(
pX = x + 31,
pY = y + 16,
type = displayType,
onPress = {
mode = TYPE_MENU_MODE
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
clearGUI()
}
), "returnTypeMenu"
)
scroll?.render(context, mouseX, mouseY, delta)
}
fun clearGUI() {
val iterator = children.iterator()
while (iterator.hasNext()) {
val (id, _) = iterator.next()
iterator.remove()
children.remove(id)
}
clearChildren()
}
override fun drawBackground(context: DrawContext, delta: Float, mouseX: Int, mouseY: Int) {
super.renderBackground(context)
val x = (width - TEXTURE_WIDTH) / 2
val y = (height - TEXTURE_HEIGHT) / 2
blitk(
matrixStack = context.matrices,
texture = TYPE_SELECTION_BASE,
x = x, // horizontal placement of GUI
y = y, // vertical placement of GUI
width = TEXTURE_WIDTH, // scale of the GUI width
height = TEXTURE_HEIGHT // scale of the GUI height
)
when (mode) {
TYPE_MENU_MODE -> drawTypeMenu(context, delta, mouseX, mouseY)
else -> drawTmSelectMenu(context, delta, mouseX, mouseY)
}
drawMoveInfo(context, delta, mouseX, mouseY)
inventory.markDirty()
addChild(
EjectButton(
pX = x + 196,
pY = y + 168,
small = false,
onPress = {
inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
val currentTm = selectedTM ?: return@EjectButton sendPacketToServer(CraftBlankTMPacket(handler.input.getStack(2)))
sendPacketToServer(
CraftTMPacket(
currentTm,
handler.input.getStack(0),
handler.input.getStack(1),
handler.input.getStack(2)
)
)
this.handler.syncState()
}
), "eject"
)
addChild(
ExitButton(
pX = x + 228,
pY = y + 185,
onPress = {
this.close()
}
), "exit"
)
val startY = y + 13
val offset = 28
var iterations = 0
CobblemonClient.storage.myParty.forEach { pokemon ->
if (pokemon != null) {
addChild(TMPartySlotWidget(
pX = x - 23,
pY = startY + (offset * iterations),
pokemon = pokemon,
onPress = {
selectedPokemon = if (selectedPokemon == pokemon) null else pokemon
}
), "partyslot_${pokemon.uuid}")
}
iterations++
}
}
override fun drawForeground(context: DrawContext?, mouseX: Int, mouseY: Int) {
//Text is usually drawn here, we dont want that
this.drawMouseoverTooltip(context, mouseX - 158, mouseY - 28)
//this.renderWithTooltip(context, mouseX, mouseY, 1F)
}
override fun onSlotUpdate(handler: ScreenHandler?, slotId: Int, stack: ItemStack?) {
}
override fun close() {
super.close()
val inventory = handler.inventory
if (inventory is TMBlockEntity.TMBlockInventory) {
inventory.tmBlockEntity.blockState.with(TMBlock.ON,false)
}
}
override fun onPropertyUpdate(handler: ScreenHandler?, property: Int, value: Int) {
}
}
@@ -0,0 +1,20 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.gui.TMMScreenHandler
import com.cobblemon.mod.common.util.giveOrDropItemStack
import net.minecraft.entity.player.PlayerEntity
import net.minecraft.inventory.Inventory
import net.minecraft.item.ItemStack
import net.minecraft.item.Items
import net.minecraft.screen.slot.Slot
class TMMOutputSlot(val screen: TMMScreenHandler, index: Int, x: Int, y: Int) : Slot(screen.playerInventory, index, x, y) {
override fun canInsert(stack: ItemStack?) = false
override fun onTakeItem(player: PlayerEntity, stack: ItemStack) {
player.giveOrDropItemStack(stack, false)
this.stack = Items.AIR.defaultStack
}
}
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.text.Text
import org.joml.Quaternionf
import org.joml.Vector3f
class TMPartySlotWidget(
pX: Number,
pY: Number,
val pokemon: Pokemon?,
onPress: PressAction
) : ButtonWidget(pX.toInt(), pY.toInt(), WIDTH, HEIGHT, Text.literal("PartyMember"), onPress, NarrationSupplier { "".text() }) {
companion object {
const val WIDTH = 46
const val HEIGHT = 27
private const val PORTRAIT_DIAMETER = 25
private val slotResource = cobblemonResource("textures/gui/tm/party_slot.png")
val genderIconMale = cobblemonResource("textures/gui/party/party_gender_male.png")
val genderIconFemale = cobblemonResource("textures/gui/party/party_gender_female.png")
}
private fun getSlotVOffset(pokemon: Pokemon?, isHovered: Boolean, isSelected: Boolean): Int {
if (isHovered || isSelected) {
if (pokemon == null) {
return 0
}
return height
}
return 0
}
override fun renderButton(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
hovered = mouseX >= x && mouseY >= y && mouseX < x + width && mouseY < y + height;
val matrices = context.matrices
blitk(
matrixStack = matrices,
texture = slotResource,
x = x,
y = y,
width = width,
height = height,
vOffset = getSlotVOffset(pokemon, isHovered, isSelected),
textureHeight = height * 2,
)
if (pokemon != null) {
val halfScale = 0.5F
// Render Pokémon
matrices.push()
matrices.translate(x + (PORTRAIT_DIAMETER / 2.0) + 10, y - 1.0, 0.0)
matrices.scale(2.5F, 2.5F, 1F)
drawProfilePokemon(
species = pokemon.species.resourceIdentifier,
aspects = pokemon.aspects.toSet(),
matrixStack = matrices,
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
state = null,
scale = 4.5F,
partialTicks = delta
)
matrices.pop()
// Draw Name
drawScaledText(
context = context,
text = pokemon.getDisplayName(),
x = x + 16,
y = y + 21.5,
scale = halfScale
)
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
val ballHeight = 22
blitk(
matrixStack = matrices,
texture = ballIcon,
x = (x - 2) / halfScale,
y = (y - 3) / halfScale,
height = ballHeight,
width = 18,
textureHeight = ballHeight * 2,
scale = halfScale
)
}
}
}
@@ -0,0 +1,158 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.tms.TechnicalMachine
import net.minecraft.client.MinecraftClient
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.AlwaysSelectedEntryListWidget
import net.minecraft.text.Text
class TMScrollingList(
val x: Int,
val y: Int,
val parent: TMMHandledScreen
) : AlwaysSelectedEntryListWidget<TMScrollingList.TMScrollingListEntry> (
MinecraftClient.getInstance(),
WIDTH, // width
HEIGHT, // height
0, // top
HEIGHT, // bottom
SLOT_HEIGHT// + SLOT_SPACING
) {
companion object {
const val WIDTH = 140
const val HEIGHT = 78
const val SLOT_WIDTH = 140
const val SLOT_HEIGHT = 20
const val SLOT_SPACING = 3
const val SCALE = 0.5f
}
private var scrolling = false
override fun getRowWidth() = SLOT_WIDTH
init {
correctSize()
setRenderHorizontalShadows(false)
setRenderBackground(false)
setRenderSelection(false)
parent.tmList.subscribeIncludingCurrent { tmList ->
val currentEntries = children()
val newTMs = tmList.filter { tm -> currentEntries.none { it.tm.id() == tm.id() } }
val removedTMs = currentEntries.filter { entry -> tmList.none { it.id() == entry.tm.id() } }
removedTMs.forEach(this::removeEntry)
newTMs.forEach { tm -> addEntry(TMScrollingListEntry(tm, parent)) }
}
}
override fun getScrollbarPositionX() = left + width - 3
private fun correctSize() {
updateSize(
WIDTH,
HEIGHT, y - 2, (y + 2) + (HEIGHT - 2))
setLeftPos(x)
}
public override fun addEntry(entry: TMScrollingListEntry) = super.addEntry(entry)
public override fun removeEntry(entry: TMScrollingListEntry) = super.removeEntry(entry)
override fun render(context: DrawContext, mouseX: Int, mouseY: Int, delta: Float) {
correctSize()
context.enableScissor(
left,
top + 5,
left + width,
top + 1 + height
)
super.render(context, mouseX, mouseY, delta)
context.disableScissor()
}
fun isHovered(mouseX: Double, mouseY: Double): Boolean {
println(mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT)))
return mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
updateScrollingState(mouseX, mouseY)
if (scrolling) {
focused = getEntryAtPosition(mouseX, mouseY)
isDragging = true
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, deltaX: Double, deltaY: Double): Boolean {
if (scrolling) {
if (mouseY < top) {
scrollAmount = 0.0
} else if (mouseY > bottom) {
scrollAmount = maxScroll.toDouble()
} else {
scrollAmount += deltaY
}
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
private fun updateScrollingState(mouseX: Double, mouseY: Double) {
scrolling = mouseX >= this.scrollbarPositionX.toDouble()
&& mouseX < (this.scrollbarPositionX + 3).toDouble()
&& mouseY >= top
&& mouseY < bottom
}
class TMScrollingListEntry(val tm: TechnicalMachine, private val parent: TMMHandledScreen) : Entry<TMScrollingListEntry>() {
private val tmButton: TMListingButton = TMListingButton(
pX = 0,
pY = 0,
onPress = {
parent.selectedTM = tm
parent.inventory.player.playSound(CobblemonSounds.GUI_CLICK, 1f, 1f)
},
tm = tm
)
override fun render(
context: DrawContext,
index: Int,
rowTop: Int,
rowLeft: Int,
rowWidth: Int,
rowHeight: Int,
mouseX: Int,
mouseY: Int,
isHovered: Boolean,
tickDelta: Float
) {
val x = rowLeft
val y = rowTop
val matrixStack = context.matrices
// Render just this TMListingButton
tmButton.setPosition(x + 1, y)
tmButton.render(context, mouseX, mouseY, tickDelta)
}
override fun mouseClicked(mouseX: Double, mouseY: Double, delta: Int): Boolean {
if (tmButton.isHovered(mouseX, mouseY)) {
tmButton.onPress()
return true
}
return false
}
override fun getNarration(): Text {
TODO("Not yet implemented")
}
}
}
@@ -0,0 +1,69 @@
package com.cobblemon.mod.common.client.gui.tm
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.TypeReturnIcon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.DrawContext
import net.minecraft.client.gui.widget.ButtonWidget
import net.minecraft.client.sound.SoundInstance
import net.minecraft.client.sound.SoundManager
class TypeButton(
pX: Int, pY: Int,
onPress: PressAction,
val type: ElementalType?
): ButtonWidget(pX, pY, WIDTH.toInt(), HEIGHT.toInt(), type?.displayName, onPress, DEFAULT_NARRATION_SUPPLIER) {
companion object {
private const val WIDTH = 24F
private const val HEIGHT = 24F
val TYPE_BUTTON = cobblemonResource("textures/gui/tm/type_button.png")
}
override fun renderButton(context: DrawContext, pMouseX: Int, pMouseY: Int, pPartialTicks: Float) {
blitk(
matrixStack = context.matrices,
texture = TYPE_BUTTON,
x = x,
y = y,
width = WIDTH,
height = HEIGHT,
vOffset = if (isHovered(pMouseX.toDouble(), pMouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
context.matrices.push()
context.matrices.scale(2f, 2f, 2f)
if (type != null) {
TypeIcon(
x = (x + 12.125) / 2,
y = (y + 3) / 2,
type = type,
centeredX = true,
small = true
).render(context)
}
else {
TypeReturnIcon(
x = (x + 12.125) / 2,
y = (y + 3) / 2,
centeredX = true,
small = true
).render(context)
}
context.matrices.pop()
}
override fun playDownSound(soundManager: SoundManager) {
}
fun isHovered(mouseX: Double, mouseY: Double) = mouseX.toFloat() in (x.toFloat()..(x.toFloat() + WIDTH)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + HEIGHT))
}
@@ -40,7 +40,8 @@ object SpawnSnowstormEntityParticleHandler : ClientNetworkPacketHandler<SpawnSno
runtime = particleRuntime,
sourceVelocity = { entity.velocity },
sourceAlive = { !entity.isRemoved },
sourceVisible = { !entity.isInvisible }
sourceVisible = { !entity.isInvisible },
entity = entity
)
storm.spawn()
@@ -0,0 +1,30 @@
package com.cobblemon.mod.common.client.net.gui
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.gui.TMMScreenHandler
import com.cobblemon.mod.common.net.messages.client.ui.CraftBlankTMPacket
import com.cobblemon.mod.common.util.playSoundServer
import net.minecraft.item.Items
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
object CraftBlankTMPacketHandler : ServerNetworkPacketHandler<CraftBlankTMPacket> {
override fun handle(packet: CraftBlankTMPacket, server: MinecraftServer, player: ServerPlayerEntity) {
val screen = player.currentScreenHandler as TMMScreenHandler
val ingredientSlot = screen.input.getStack(2)
if (ingredientSlot.isOf(Items.AMETHYST_SHARD) && ingredientSlot.count >= 1) {
screen.input.removeStack(2, 1)
screen.result.setStack(0, CobblemonItems.BLANK_TM.defaultStack)
screen.input.markDirty()
screen.result.markDirty()
player.currentScreenHandler.syncState()
player.world.playSoundServer(player.pos, CobblemonSounds.TMM_CRAFT_BLANK, SoundCategory.BLOCKS)
}
}
}
@@ -0,0 +1,60 @@
package com.cobblemon.mod.common.client.net.gui
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.gui.TMMScreenHandler
import com.cobblemon.mod.common.item.TechnicalMachineItem
import com.cobblemon.mod.common.net.messages.client.ui.CraftTMPacket
import com.cobblemon.mod.common.util.playSoundServer
import net.minecraft.item.Items
import net.minecraft.registry.Registries
import net.minecraft.server.MinecraftServer
import net.minecraft.server.network.ServerPlayerEntity
import net.minecraft.sound.SoundCategory
object CraftTMPacketHandler : ServerNetworkPacketHandler<CraftTMPacket> {
override fun handle(packet: CraftTMPacket, server: MinecraftServer, player: ServerPlayerEntity) {
val screen = player.currentScreenHandler as TMMScreenHandler
val discSlot = screen.inventory?.getStack(0)
val gemSlot = screen.inventory?.getStack(1)
val ingredientSlot = screen.inventory?.getStack(2)
val outputSlot = screen.result.getStack(0)
val typeGem = Registries.ITEM.get(ElementalTypes.get(packet.tm.type)?.typeGem)
print("test")
if (!outputSlot.isEmpty) return
if (discSlot != null) {
if (!discSlot.isOf(CobblemonItems.BLANK_TM)) return
}
if (gemSlot != null) {
if (!gemSlot.isOf(typeGem)) return
}
if (packet.tm.recipe != null) {
if (ingredientSlot != null) {
if (!ingredientSlot.isOf(Registries.ITEM.get(packet.tm.recipe.item))) return
}
if (ingredientSlot != null) {
if (ingredientSlot.count < packet.tm.recipe.count) return
}
}
val item = CobblemonItems.TECHNICAL_MACHINE.defaultStack
item.getOrCreateNbt().putString(TechnicalMachineItem.STORED_MOVE_KEY, packet.tm.id().toString())
screen.result.setStack(0, item)
screen.inventory?.removeStack(0, 1)
screen.inventory?.removeStack(1, 1)
if (packet.tm.recipe != null) {
screen.inventory?.removeStack(2, packet.tm.recipe.count)
}
screen.input.markDirty()
screen.result.markDirty()
player.currentScreenHandler.syncState()
player.world.playSoundServer(player.pos, CobblemonSounds.TMM_CRAFT, SoundCategory.BLOCKS)
}
}
@@ -0,0 +1,13 @@
package com.cobblemon.mod.common.client.net.gui
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.client.ui.InteractPokemonUIPacket
import com.cobblemon.mod.common.net.messages.client.ui.OpenTMMPacket
import net.minecraft.client.MinecraftClient
object OpenTMMPacketHandler : ClientNetworkPacketHandler<OpenTMMPacket>
{
override fun handle(packet: OpenTMMPacket, client: MinecraftClient) {
//client.setScreen()
}
}

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