Compare commits

..
Author SHA1 Message Date
Polymeta da76b2fb94 license
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-31 18:24:08 +00:00
Polymeta 87d8197dd0 Merge branch 'bag-of-plenty' into 'version/1.9'
Bag

See merge request cable-mc/cobblemon!2433
2026-10-30 23:58:31 +00:00
Polymeta 2ed6f89d09 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:57:57 +00:00
Polymeta 5947468cd9 Merge branch 'version/1.9' into bag-of-plenty
# Conflicts:
#	common/src/main/java/com/cobblemon/mod/common/mixin/PlayerMixin.java
#	common/src/main/java/com/cobblemon/mod/common/mixin/ServerPlayerMixin.java
#	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/world/gamerules/CobblemonGameRules.kt
#	common/src/main/resources/cobblemon-common.accesswidener
2026-10-30 23:55:24 +00:00
Polymeta 7ba1507ac8 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:51:39 +00:00
Polymeta 9fdffdc392 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:51:03 +00:00
Polymeta 6a01fe3863 Merge branch 'feature/more-trades' into 'version/1.9'
Feature/more trades

See merge request cable-mc/cobblemon!2208
2026-10-30 23:50:51 +00:00
Polymeta 9b5a153e3f Merge branch 'main' into version/1.9
# Conflicts:
#	gradle.properties
2026-10-30 23:48:34 +00:00
Polymeta 67f906dec2 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:47:43 +00:00
Polymeta 0570afbe05 Merge branch 'main' into 'main'
Fix habitat structure misspelling

Closes #2132

See merge request cable-mc/cobblemon!2500
2026-10-30 23:46:30 +00:00
Rayegun dfba2d27ab Fix habitat structure misspelling 2026-10-30 23:46:29 +00:00
Polymeta 9c7cd4b616 Merge branch 'healer-spawn-point' into 'main'
Healer spawn point

See merge request cable-mc/cobblemon!2489
2026-10-30 23:43:30 +00:00
Polymeta 9fe0e9c87e [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:40:22 +00:00
Polymeta bd0e5a44bd Merge branch 'feat/amethyst-core' into 'main'
feat/amethyst core

See merge request cable-mc/cobblemon!2505
2026-10-30 23:39:37 +00:00
Polymeta 229ec4176b [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:38:54 +00:00
Polymeta 323413efdc Merge branch 'fix/ponigiri-food-values-appleskin' into 'main'
fix: correct ponigiri values for AppleSkin

See merge request cable-mc/cobblemon!2513
2026-10-30 23:35:37 +00:00
Polymeta a309cc2522 [skip-ci] changelog cleanup
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-30 23:33:53 +00:00
Polymeta cb782a2aa1 Merge branch 'steppy-steps' into 'main'
Pokémon Stepsounds

Closes #2107

See merge request cable-mc/cobblemon!2432
2026-10-30 23:30:31 +00:00
Sterrezo 2c58dcb8ef Update CHANGELOG.md 2026-10-31 00:31:51 +01:00
Sterrezo d91cd4322c Merge branch 'main' into steppy-steps 2026-10-31 00:29:14 +01:00
whatsy e48e492392 Update CHANGELOG.md 2026-10-27 23:45:20 -04:00
whatsy 1f93994eba Merge branch 'main' into healer-spawn-point
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/world/gamerules/CobblemonGameRules.kt
2026-10-27 23:45:02 -04:00
whatsy 5210121b3d Update CHANGELOG.md 2026-10-27 23:36:23 -04:00
Gitoido Okaski 7fa3bf24d1 chore: update explanation skip_cl 2026-10-28 03:10:34 +00:00
Gitoido Okaski 1128d7edf3 fix: appleskin gradle stuff for runtime 2026-10-28 03:10:34 +00:00
Gitoido Okaski eef50a7237 fix: correct ponigiri values for AppleSkin 2026-10-28 03:10:34 +00:00
Ronak Chavva 397d3c618d reverted amythest pokespawn condition 2026-10-23 18:28:22 -05:00
Ronak Chavva 29f6feab6c Merge branch 'main' of https://gitlab.com/ronakch8/cobblemon into feat/amethyst-core 2026-10-23 18:09:35 -05:00
Ronak Chavva ea5d3ae40f amethyst grows on core 2026-10-23 18:09:11 -05:00
whatsy def975c03f Add bag sounds 2026-10-17 19:08:15 -04:00
Sterrezo e67567e8dc Update CobblemonSounds.kt 2026-10-17 23:25:34 +02:00
Sterrezo 23a42a29b3 Added bag sounds for implementation 2026-10-17 23:22:19 +02:00
whatsy bacc482bf0 finishing touches 2026-10-14 16:25:16 -04:00
Gertimoshka edb3f906f8 Simpler Mixin 2026-10-14 21:48:14 +02:00
whatsy 4171bf341b Add config option to allow using healing machines as a respawn point 2026-10-14 15:02:54 -04:00
Gitoido 61d53f3052 Merge branch 'main' into 'steppy-steps'
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt
2026-10-09 16:19:32 +00:00
saultsc 2e88d971f7 docs: detail multi-Pokémon trading and trade UI changes in the changelog 2026-10-04 14:34:16 -04:00
saultsc bfc1c66097 fix: log trade start at info level instead of error 2026-10-04 14:34:16 -04:00
saultscandClaude Opus 5.5 13d9f84d8d Merge branch 'main' into feature/more-trades
Resolve conflicts with the trade dupe fix and per-OT trade friendship
caching by applying both to every Pokémon in a multi-Pokémon trade.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 10:55:28 -04:00
saultsc 148eb130d9 ci: install findutils in pipeline jobs and sync CI config with main 2026-10-04 10:11:00 -04:00
saultsc eb07b0e03f docs: document trade evolutions checking every Pokémon received in the changelog 2026-10-04 10:05:51 -04:00
saultsc 1b8d416962 fix: evaluate trade evolutions against every Pokémon received in a multi-Pokémon trade 2026-10-04 10:05:27 -04:00
saultsc 3030b544b5 refactor: migrate trade advancement and stat handlers to the list-based TradeEvent API 2026-10-04 10:05:21 -04:00
saultsc b890d662f6 feature: include every traded Pokémon in TradeEvent.Pre and TradeEvent.Post 2026-10-04 10:05:20 -04:00
saultsc 2a9bbc1d68 fix: log a warning when a trade is performed with an empty offer 2026-10-04 10:05:13 -04:00
dhi_holo 735817869a Forgot a rename 2026-09-28 16:34:56 +10:00
dhi_holo 488e6dcf7a ID Corrections and Recipes 2026-09-28 16:16:51 +10:00
dhi_holo a0f32ea444 Models and Translations 2026-09-28 15:19:53 +10:00
dhi_holo b426df7271 A Metric Buttton of Tagging
Added a bunch of tags, IDs, and redundancies, it doesn't cover everything but it should cover a lot of the base game without much worry
2026-09-28 10:29:50 +10:00
Sterrezo 1543e06a06 Update CHANGELOG.md 2026-09-27 14:56:17 +02:00
Sterrezo d7b4ccb1e5 Update corviknight.animation.json 2026-09-27 14:46:07 +02:00
Sterrezo dedd449329 Update corviknight.animation.json 2026-09-27 14:45:31 +02:00
Sterrezo 38c589658f Finalized assets and added amethyst stepsounds it'S FINALLY DONE 2026-09-27 14:44:29 +02:00
Gertimoshka 182e06205d Idiocity Fixes 2026-09-27 13:59:26 +02:00
Gertimoshka 4c391645fe Bag Drop Gamerule to drop all bag items, plus some rendering changes 2026-09-27 13:18:03 +02:00
Gertimoshka 79bf50004a Merge branch 'main' into bag-of-plenty 2026-09-27 11:51:06 +02:00
Sterrezo 2f961bd7bf Expanded on all stepsounds to cover metal surfaces 2026-09-26 23:52:15 +02:00
Sterrezo b72244a986 Added large and medium claw stepsounds for glass surfaces 2026-09-26 19:42:06 +02:00
Sterrezo 6710f25190 Added medium and large claw stepsounds for mud, wood and snow surfaces, updated mud hoof stepsounds 2026-09-26 15:44:32 +02:00
Sterrezo 4089e5d7a9 Added medium and large claw stepsounds for dirt and sand surfaces, updated stone claw stepsounds 2026-09-26 11:54:45 +02:00
Sterrezo 0a49b5b17b Added medium and large claw stepsounds for stone surfaces, updated grass claw stepsounds 2026-09-25 16:57:06 +02:00
Sterrezo 34c40de429 Suppressed Pidgeot's and Corviknight's Minecraft stepsounds 2026-09-25 12:03:03 +02:00
Sterrezo 6d02144401 Updated grass claw stepsound assets 2026-09-25 12:01:42 +02:00
Sterrezo 561687ce0f Added stepsounds to Pidgeot's ride walk animations 2026-09-25 11:30:28 +02:00
Sterrezo 20973c6724 Added stepsound keyframes to Pidgeot, added medium and large claw stepsounds for grass surfaces 2026-09-24 22:44:15 +02:00
Sterrezo bf10a4dcef Added medium and large hoof stepsounds for glass and ice surfaces 2026-09-24 21:23:37 +02:00
Sterrezo 565987a780 Added standard medium and large stepsounds for glass/ice surfaces, updated mud hoof sounds 2026-09-23 14:49:34 +02:00
Sterrezo 4f9a6c8e98 Added medium and large hoof stepsounds for mud surfaces 2026-09-23 10:46:56 +02:00
Sterrezo 3a3e92d441 Update CHANGELOG.md 2026-09-23 00:38:26 +02:00
Sterrezo bc0dc809f9 Merge branch 'main' into steppy-steps 2026-09-23 00:37:33 +02:00
Sterrezo 92310847aa Footsteps now check from depth 0 instead of 1 2026-09-23 00:08:37 +02:00
Polymeta 5bdc59830c change standingOn logic to consider non-full blocks
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-09-22 22:44:09 +01:00
Sterrezo 17f524d3da Added standard medium and large stepsounds for mud surfaces 2026-09-22 18:45:29 +02:00
Sterrezo 4d0de2bb45 Corrected the size sound variation for leaves stepsounds 2026-09-22 15:52:05 +02:00
Sterrezo b3b8f56bb8 Made block tags optional 2026-09-22 15:45:46 +02:00
Sterrezo f036b83693 Added universal stepsounds for leaves 2026-09-22 15:34:17 +02:00
Sterrezo 73fb31c2fc Added hoof stepsounds for wood surfaces 2026-09-22 10:49:03 +02:00
Sterrezo 381787f39d Updated standard wood stepsounds 2026-09-22 10:24:12 +02:00
Sterrezo 2cb942a24b Merge branch 'main' into steppy-steps 2026-09-10 21:26:50 +02:00
Sterrezo c79cb5731e Missclicked 2026-09-10 18:14:43 +02:00
Sterrezo 4760bb15a4 Added standard medium and large footsteps for wood surfaces 2026-09-10 18:14:28 +02:00
Sterrezo aac881f23c Added large and medium hoof footstep sounds 2026-09-09 18:31:27 +02:00
Sterrezo da19c51566 Implemented standard medium and large snow footsteps 2026-09-09 17:58:27 +02:00
Paul 396ab13164 1.9 in gradle properties 2026-09-06 20:01:10 +10:00
Sterrezo 37dd354155 Merge branch 'main' into steppy-steps 2026-09-06 11:12:32 +02:00
Sterrezo acb1ebda19 Finalized hoof sounds 2026-05-27 12:34:57 +02:00
Sterrezo 5dddc05626 Merge branch 'main' into steppy-steps 2026-05-27 00:16:42 +02:00
Sterrezo 99890671bd Added hoof footsteps for sand surfaces and updated other hoof sounds 2026-05-26 22:21:56 +02:00
Sterrezo 9451ac5f46 Merge branch 'main' into steppy-steps 2026-05-26 21:42:41 +02:00
Sterrezo a8e4e21ba1 Merge branch 'main' into steppy-steps 2026-05-19 14:15:48 +02:00
Sterrezo f555ba3703 Made Molang slightly less cursed with proper random functions 2026-05-16 14:24:18 +02:00
Sterrezo 37677b3730 Asset updates, added hoof on stone sounds 2026-05-16 12:17:24 +02:00
Sterrezo 2a298b2268 Merge branch 'main' into steppy-steps 2026-05-16 11:01:28 +02:00
Sterrezo ad3f94fb80 Added hoof grass medium and large sounds 2026-05-16 02:27:46 +02:00
Sterrezo 719660798e Started hoof sound implementation with Wyrdeer, asset and keyframe updates 2026-05-15 18:17:04 +02:00
Sterrezo 8d973a09e1 Tweaks to the footstep function logic to better account for depth
If depth is 2 outright, the function could pick a block below the actual block the entity is standing on if it was checked earlier in the function. Loop iterates twice, so now it plays footsteps even when standing on the edge of a block or running down slopes.
2026-05-15 11:48:50 +02:00
Sterrezo 3579a86cde Merge branch 'main' into steppy-steps 2026-05-15 11:08:00 +02:00
Sterrezo ba96a6a205 Added standard medium and large sand footsteps 2026-05-14 20:18:04 +02:00
Sterrezo f07d2a9a9a First implementation of footstep keyframes with Arcanine 2026-05-14 16:56:13 +02:00
Sterrezo 2469067fd4 Allow for silencing step sounds within the species file 2026-05-14 02:24:18 +02:00
saultsc c10c91d86a changes add to changelog 2026-04-23 11:21:30 -04:00
saultsc d5fd2e9a07 fix to reorder now is good :) 2026-04-22 22:37:24 -04:00
saultsc 45cc0ba579 trader hover select poing more tall z 2026-04-22 22:12:38 -04:00
saultsc 1880a0a24e reorder the party if you have a blank space in the middle 2026-04-22 19:59:55 -04:00
saultsc 5f3a0b96cc summary show is changed for a hover 2026-04-22 19:46:11 -04:00
saultsc 7ab1610700 remove the hover to the opposing party 2026-04-20 23:06:50 -04:00
saultsc daad9f6c00 add trade party selected assets and apply in trade ui 2026-04-20 22:52:08 -04:00
saultsc 2b234b740e trade animation directed towards the pokeball 2026-04-20 21:49:14 -04:00
saultsc 39e3983020 Prevent exiting summary view when clicking the active Pokémon 2026-04-20 21:17:20 -04:00
saultsc 9d58cdb21c deleting a suspicius comment 2026-04-20 03:07:18 -04:00
saultsc f05e35e792 Improve trade GUI interaction, modify summary interaction style 2026-04-20 02:59:10 -04:00
saultsc a333e68620 Update trade network packets and handlers 2026-04-20 02:58:15 -04:00
saultsc 2c2791ec69 Refactor trade list<Pokemon> flow on server 2026-04-20 02:57:13 -04:00
terrainwax 635b09d82f add bag rendering 2026-04-16 17:22:45 +02:00
Gertimoshka 46c3ff9db4 first attempt to make bag render 2026-04-16 16:51:01 +03:00
Gertimoshka 8bfc95c23a Items now render in multiple stacks then there are several stacks worth of same item 2026-04-16 10:41:11 +03:00
Gertimoshka 99d05b6ef7 More Tag Fixes and TM Category 2026-04-16 09:51:48 +03:00
Gertimoshka 1c0a14c819 Merging 1.8.0 into bag-of-plenty 2026-04-15 23:30:11 +03:00
Gertimoshka fe5cd58801 Bag Categories are now Datapackable 2026-04-15 23:24:39 +03:00
Gertimoshka c3bd007f5a Party Slots now render in the GUI, and held items can be given and taken, and items can be used on the pokemon from the GUI 2026-04-13 11:12:46 +03:00
Gertimoshka dba24e104e Reduced unnecessary nbt data saved for bag 2026-04-10 22:40:56 +03:00
Gertimoshka 35815e371b Bag can now only hold a specific amount of items based on Categories 2026-04-10 22:26:50 +03:00
Gertimoshka d0f8ae0565 Categories Filtering and Scrollbar Work 2026-04-10 19:13:59 +03:00
Gertimoshka b2e21969ca Partial Implementation of Bag Categories 2026-04-10 00:59:31 +03:00
Gertimoshka ea57c233a3 Bag data is now stored on player and saved in the nbt 2026-04-08 23:06:46 +03:00
Gertimoshka 0d39b47f22 Bag Container now properly moves items in and out of Container 2026-04-08 21:08:01 +03:00
Gertimoshka 9f3a279b03 The hotmess of slotless container 2026-04-07 12:00:50 +03:00
627 changed files with 4819 additions and 1081 deletions

No files matched your search

+24 -269
View File
@@ -1,282 +1,37 @@
# Changelog
## [1.8.x (MONTH xth, 2026)](#1-8-x)
## [1.9.0 (MONTH xth, 2026)](#1-9-0)
### Additions
- Added riding statistics: `Distance by Pokémon on Land`, `Distance by Pokémon in Air` and `Distance by Pokémon in Liquid`.
- Added the Partner Mark, which party Pokémon have a very small chance to earn as the player walks, provided they have enough friendship.
- Added trades for Saccharine Saplings, Hearty Grains, Chipped Pot, and Masterpiece Teacup to the Wandering Trader.
- Added brewing recipe for Throat Spray.
- Added mood particles! Pokémon now visually react to certain actions:
- Angry particles when attacking a mob.
- Annoyed particles when a player tries to feed them while they’re already full.
- Sweat droplets when they run out of stamina while being ridden.
- Added config options to award experience to fainted Pokémon for opponents they helped defeat (`awardExperienceToFaintedPokemon`) and to still award experience for defeated enemies even after battle loss/forfeit (`awardExperienceOnBattleLoss`)
- Party Overlay Exp Gained Animation will now display the amount of EXP the Pokémon gained
- Added a toast to notify players of how they can scroll through their party. Did you know you can hold R and scroll instead of using arrow keys??!
- Added a new layer property that makes a layer scroll in a direction. "scrolling": {"speedU": 0.1, "speedV": 0.1}
- Added missing crops to Botany Pots integration
- Added a way to obtain Porygon by inserting the Upgrade or Dubious Disc into a data monitor
- Added a new `cobblemon:structure`, which is a feature without any restrictions allowing the Builders to place certain structures in much more specific manner.
- Added Configs for Infinite Use TMs and Unlocking all TMs
- Added Type Gem blocks
- Added Type Gem Clusters
- Added TMs
- Added TM Machine
- Added Deepslate Core Block
- Added /givetm Command
- Added advancement triggers for learning TMs: `cobblemon:has_learn_specific_tm` (per-TM), `cobblemon:has_learn_all_tm` (all TMs learned), and `cobblemon:tms_learned` (count-based), backed by a new `totalTMLearnedCount` on player advancement data.
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` tracking for capturing Alpha Pokémon.
- Added Move Dex to Pokédex
- Added the Habitat Block, a spawner block for servers and adventure maps that controls spawning in an area.
- Added Disk Rack for storing disks and being an audio sequencer for Noteblocks
- Added intrinsic scaling to all Pokémon (+-5%). Some rendering cases, such as shoulder mounting, revert to 1.0 scaling to preserve animation accuracy.
- Added scaling to "baby" Pokémon (smaller (-20%) at level 1 → normally sized at level 10)
- Added conditional seats for riding. Seats can be gated by molang such as "is_alpha".
- Added Alpha Pokémon
- Added alpha scaling (alphas are larger than their normal counterparts)
- Alphas will always lead herds and will spawn in herds
- Alphas spawn with two random TM moves from their learnset
- Alpha Pokémon will always defend themselves from players that attack them
- Added alpha level matching and stat boost (based on level)
- Added pitch-down and reverb to alpha cries
- Added alpha Pokémon bait effect
- Added alpha eye rendering including eye overlay, bloom, eye trail, and particles.
- Added cobweb block slowness immunity for spider Pokémon
- Added 49 new habitat structures. 34 as typical Minecraft structures and 15 as structures that get placed as biome features to allow for more natural placement in generation. Structures placed as biome features will not be able to be located with the `/locate structure` command.
- Vanilla village house template pools have been adjusted to account for the new abandoned village structure habitat. Weights have changed within the JSON file for every piece to ensure too many habitats don't generate in a single village.
- Added 10 new ruin structures.
- rooted_arch_ruins & crumbling_arch_ruins have been transferred over to biome features, allowing for better placement in generation.
- All ruin structures that did not generate with a gimmighoul/gilded chest before will now generate with one.
- All ruin structures gilded chest loot tables have been updated to include a guaranteed TM not learned naturally by any Pokémon through their level-up moveset. Each ruin gets its own specific TM, so you can now go hunting ruins to complete your move dex!
- All fossil structures have been transferred over to biome features, allowing for better placement in generation.
- The prehistoric_underwater_fissure & prehistoric_submerged_impact fossil structures now use the biome tag `#cobblemon:is_temperate_ocean`.
- Gimmighoul towers now generate at ground level rather than sinking one block into the ground.
- A new shipwreck cove structure called the "magma_shipwreck_cove" has been added.
- Shipwreck coves now generate slightly more frequently.
- Added equipment loot tables for shipwreck cove enemies.
- Maps from abandoned fishing boats leading to shipwreck coves now will skip explored chunks, allowing the player to find more shipwreck coves easier.
- The lush shipwreck cove's phantom mob spawner has been replaced with breezes.
- The lush shipwreck cove structure itself has been slightly cleaned up to be less noisy with the blocks it is built out of.
- All shipwreck coves' spawners have been adjusted to give compatible mobs enchanted iron or diamond armour and weapons while in its ominous state. The armour given to the mobs will also use the automaton trim. We upped the difficulty because it was too easy now that your Pokémon can fight for you.
- One TM of the elemental hyper beams has been added as guaranteed loot to the main treasure chest in every shipwreck cove. (Magma cove gets blast burn, lush cove gets frenzy plant, and submerged cove gets hydro cannon)
- Added 7 new advancements
- Pressing the inventory key now closes the Pokémon interact wheel GUI
- Pokémon cries now play when clicking Pokémon in the summary screen.
- Pressing the Summary key while either the starter selection or summary screen are open now closes those screens
- Ice type Pokémon are now immune to Packed Snow
- Held item visibility can now be controlled by the held_item_visible command argument.
### New rideable Pokémon
- Crobat
- Skarmory
- Milotic
- Archeops
- Goodra
- Goodra (Hisuian Form)
- Drampa
- Duraludon
- Archaludon
### Pokémon Added
#### Generation 3
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Skitty
- Delcatty
- Seviper
- Zangoose
#### Generation 4
- Cherubi
- Cherrim
- Stunky
- Skuntank
#### Generation 5
- Audino
- Pawniard
- Bisharp
#### Generation 6
- Pancham (+ Cosmetic with Black Glasses)
- Pangoro (+ Cosmetic with Black Glasses)
#### Generation 7
- Rockruff
- Lycanroc
#### Generation 8
- Rolycoly
- Carkol
- Coalossal
- Runerigus
- Snom
- Frosmoth
- Indeedee
- Duraludon
- Applin
- Flapple
- Appletun
#### Generation 9
- Nymble
- Lokix
- Kingambit
- Dipplin
- Archaludon
- Hydrapple
#### Regional Forms
- Sandshrew (Alolan Form)
- Sandslash (Alolan Form)
- Yamask (Galarian Form)
- Avalugg (Hisuian Form)
### Model updates for the following Pokémon
- Sandshrew
- Sandslash
- Nidoking
- Zubat
- Golbat
- Crobat
- Ditto
- Porygon
- Porygon2
- Porygon-Z
- Skarmory
- Smoochum
- Jynx
- Popplio
- Brionne
- Primarina
- Fletchling
- Fletchinder
- Talonflame
- Sableye
- Mawile
- Goomy
- Sliggoo
- Goodra
- Nacli
- Naclstack
- Garganacl
- Bidoof
- Cranidos
- Chatot
- Yamask
- Cofagrigus
- Tyranitar
- Turtonator
- Golett
- Corsola
- Arbok (New Patterns: Angry, Cartoon, Clown, Comedic, Crow, Grin, Ink, Modern, Rain, Scribble, Silence, Sloth, Suspicious, Will)
- ... and Substitute
#### Model changes to the regional bias resource pack:
- Added Mime Jr. (Galar Bias)
- Added Bergmite (Hisui Bias)
- Changed models for every existing regional bias
### Animation updates for the following Pokémon
- Kakuna
- Grimer
- Nidoran♀ (+ New shoulder mount)
- Nidorina
- Nidoran♂ (+ New shoulder mount)
- Sandshrew
- Diglett
- Dugtrio
- Graveler
- Happiny
- Chansey
- Blissey
- Poliwhirl
- Poliwrath
- Zubat
- Vulpix
- Drowzee
- Hypno
- Noctowl
- Skarmory
- Skitty
- Flygon
- Shellos
- Riolu
- Lucario
- Purrloin
- Liepard
- Joltik
- Galvantula
- Nidoking
- Fletchling
- Fletchinder
- Talonflame
- Archeops
- Drampa
- Sewaddle
- Swadloon
- Leavanny
- Goomy
- Sliggoo
- Goodra
- Shedinja
- Skwovet
- Greedent
- Bidoof
- Cranidos
- Chatot
- Yamask
- Cofagrigus
- Luvdisc
- Staryu
- Starmie
- Togetic
- Rellor
- Rabsca
- Yungoos
- Gumshoos
- Cottonee
- Whimsicott
- Corsola
#### Added sounds to the following moves
- Absorb
- Aurora Beam
- Double Team
- Draco Meteor
- Fire Punch
- Fire Spin
- Flame Charge
- Flame Wheel
- Giga Drain
- Ice Punch
- Leech Life
- Magical Leaf
- Mega Drain
- Minimize
- Poison Gas
- Rock Throw
- Scary Face
- Seed bomb
- Smokescreen
- String Shot
- Tail Whip
- Trainer bags and packs got added
- You can craft and equip those to store additional items
### Changes
- Trading has been overhauled to allow offering multiple Pokémon, up to the entire party, in a single trade
- Selected Pokémon are now highlighted in both parties with new party slot assets
- Hovering an offered Pokémon now shows its summary, and clicking the active Pokémon no longer exits the summary view
- The trade animation now moves the offered Pokémon towards the Poké Ball
- Parties are compacted after a trade so no empty slots are left between Pokémon
### Fixes
### Developer
- Trade network packets (`TradeUpdatedPacket`, `UpdateTradeOfferPacket`, and `TradeCompletedPacket`) now receive an array of Pokémon instead of a single Pokémon to execute trades
- `TradeEvent.Pre` and `TradeEvent.Post` now expose every Pokémon each participant receives (`tradeParticipant1Pokemon` and `tradeParticipant2Pokemon` are now lists), and their Molang context includes `trade_participant_1_pokemon_list` and `trade_participant_2_pokemon_list`
### Molang & Datapacks
- `bag_categories` got added which control what kind of tabs and capabilities the Trainerbags have
```json
{
"order": 1,
"icon": "cobblemon:textures/item/medicine/potion.png",
"whiteListTag": "cobblemon:bag/whitelist_consumables",
"blackListTag": "cobblemon:bag/blacklist_consumables",
"maxCount": 512
}
```
- Bunch of default tags as white/blacklist got added to control what can be inserted the corresponding bag categories
### Particles
### Localization
- Updated translations for:
- Updated translations for:
@@ -0,0 +1,16 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.bag;
import com.cobblemon.mod.common.item.bag.CobblemonBagContainer;
public interface BagEntity {
CobblemonBagContainer cobblemon$getBagContainer();
void cobblemon$setBagContainer(CobblemonBagContainer bagContainer);
}
@@ -23,6 +23,8 @@ import com.cobblemon.mod.common.api.tags.CobblemonItemTags;
import com.cobblemon.mod.common.duck.PlayerDuck;
import com.cobblemon.mod.common.duck.RidePassenger;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.bag.BagEntity;
import com.cobblemon.mod.common.item.bag.CobblemonBagContainer;
import com.cobblemon.mod.common.pokedex.scanner.PokedexEntityData;
import com.cobblemon.mod.common.pokedex.scanner.ScannableEntity;
import com.cobblemon.mod.common.pokemon.FormData;
@@ -56,6 +58,7 @@ import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.enchantment.EnchantmentHelper;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Vector3f;
import org.objectweb.asm.Opcodes;
@@ -73,7 +76,7 @@ import java.util.*;
import java.util.stream.Collectors;
@Mixin(Player.class)
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger {
public abstract class PlayerMixin extends LivingEntity implements ScannableEntity, OrientationControllable, PlayerDuck, RidePassenger, BagEntity {
@Unique
private Vector3f cobblemon$driverInput;
@@ -107,6 +110,8 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
@Unique private Vec3 cobblemon$rideEyePos = Vec3.ZERO;
@Unique private CobblemonBagContainer cobblemon$cobblemonBagContainer = new CobblemonBagContainer();
protected PlayerMixin(EntityType<? extends LivingEntity> p_20966_, Level p_20967_) {
super(p_20966_, p_20967_);
}
@@ -404,4 +409,37 @@ public abstract class PlayerMixin extends LivingEntity implements ScannableEntit
original.call(target, strength, x, z);
}
@Override @NotNull
public CobblemonBagContainer cobblemon$getBagContainer() { return cobblemon$cobblemonBagContainer; }
@Override
public void cobblemon$setBagContainer(CobblemonBagContainer bagContainer) { cobblemon$cobblemonBagContainer = bagContainer; }
@Inject(
method = "readAdditionalSaveData(Lnet/minecraft/nbt/CompoundTag;)V",
at = @At("TAIL")
)
private void cobblemon$readAdditionalSaveData(CompoundTag compound, CallbackInfo ci) {
if (compound.contains("CobblemonBagData", 9)) {
this.cobblemon$cobblemonBagContainer.fromTag(compound.getList("CobblemonBagData", 10), this.registryAccess());
}
}
@Inject(
method = "addAdditionalSaveData(Lnet/minecraft/nbt/CompoundTag;)V",
at = @At("TAIL")
)
private void cobblemon$addAdditionalSaveData(CompoundTag compound, CallbackInfo ci) {
compound.put("CobblemonBagData", this.cobblemon$cobblemonBagContainer.createTag(this.registryAccess()));
}
@Inject(
method = "dropEquipment()V",
at = @At("TAIL")
)
private void cobblemon$dropCobblemonBag(CallbackInfo ci) {
if (level().getGameRules().getBoolean(CobblemonGameRules.BAG_DROP_ITEMS)) {
this.cobblemon$cobblemonBagContainer.dropAll((Player)(Object)this);
}
}
}
@@ -15,17 +15,23 @@ import com.cobblemon.mod.common.api.riding.RidingStyle;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawner;
import com.cobblemon.mod.common.api.spawning.spawner.PlayerSpawnerFactory;
import com.cobblemon.mod.common.api.stats.CobblemonStats;
import com.cobblemon.mod.common.block.HealingMachineBlock;
import com.cobblemon.mod.common.duck.ServerLevelDuck;
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity;
import com.cobblemon.mod.common.bag.BagEntity;
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules;
import com.llamalad7.mixinextras.injector.ModifyExpressionValue;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.core.BlockPos;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -33,6 +39,9 @@ import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.Optional;
@Mixin(ServerPlayer.class)
public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
@@ -142,4 +151,25 @@ public abstract class ServerPlayerMixin implements PlayerSpawnerAccessor {
((ServerLevelDuck) this.serverLevel()).cobblemon$getLevelResourceLocation()
);
}
@Inject(
method = "findRespawnAndUseSpawnBlock(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;FZZ)Ljava/util/Optional;",
at = @At(value = "HEAD"),
cancellable = true
)
private static void cobblemon$useHealthStationAsRespawn(ServerLevel level, BlockPos pos, float angle, boolean forced, boolean keepInventory, CallbackInfoReturnable<Optional<ServerPlayer.RespawnPosAngle>> cir) {
BlockState state = level.getBlockState(pos);
Block block = state.getBlock();
if (block instanceof HealingMachineBlock && HealingMachineBlock.Companion.canSetSpawn(level)) {
cir.setReturnValue(HealingMachineBlock.Companion.findStandUpPosition(EntityType.PLAYER, level, pos).map((vec3) -> ServerPlayer.RespawnPosAngle.of(vec3, pos)));
}
}
@Inject(
method = "restoreFrom",
at = @At("TAIL")
)
private void cobblemon$restoreCobblemonBag(ServerPlayer that, boolean keepEverything, CallbackInfo ci) {
((BagEntity)(Object)this).cobblemon$setBagContainer(((BagEntity)that).cobblemon$getBagContainer());
}
}
@@ -26,6 +26,7 @@ import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.entity.boat.CobblemonBoatType
import com.cobblemon.mod.common.item.*
import com.cobblemon.mod.common.item.armor.CobblemonArmorTrims
import com.cobblemon.mod.common.item.bag.CobblemonBagItem
import com.cobblemon.mod.common.item.battle.DireHitItem
import com.cobblemon.mod.common.item.battle.GuardSpecItem
import com.cobblemon.mod.common.item.battle.XStatItem
@@ -1577,6 +1578,12 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
@JvmField
val LUCIBELL = compostableBlockItem("lucibell", CobblemonBlocks.LUCIBELL, 0.85f)
//Bags
@JvmField
val TRAINER_BAG = this.create("trainer_bag", CobblemonBagItem())
@JvmField
val TRAINER_PACK = this.create("trainer_pack", CobblemonBagItem())
private fun blockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): BlockItem = this.create(name, BlockItem(block, Item.Properties().rarity(rarity)))
private fun itemNameBlockItem(name: String, block: Block, rarity: Rarity = Rarity.COMMON): ItemNameBlockItem = this.create(name, ItemNameBlockItem(block, Item.Properties().rarity(rarity)))
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common
import com.cobblemon.mod.common.block.campfirepot.CookingPotMenu
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
import com.cobblemon.mod.common.block.tmmachine.TMMachineMenu
import com.cobblemon.mod.common.platform.PlatformRegistry
import net.minecraft.core.Registry
@@ -20,8 +21,9 @@ import net.minecraft.world.inventory.MenuType
object CobblemonMenuType : PlatformRegistry<Registry<MenuType<*>>, ResourceKey<Registry<MenuType<*>>>, MenuType<*>>() {
val COOKING_POT = MenuType.register("cooking_pot_menu", ::CookingPotMenu)
val COOKING_POT = MenuType.register("cooking_pot", ::CookingPotMenu)
val TM_MACHINE = MenuType.register("tm_machine_menu", ::TMMachineMenu)
val COBBLEMON_BAG = MenuType.register("cobblemon_bag", ::CobblemonBagMenu)
override val registry: Registry<MenuType<*>>
get() = BuiltInRegistries.MENU
@@ -13,6 +13,8 @@ import com.cobblemon.mod.common.client.net.OpenBehaviourEditorHandler
import com.cobblemon.mod.common.client.net.PlayerInteractOptionsHandler
import com.cobblemon.mod.common.client.net.SetClientPlayerDataHandler
import com.cobblemon.mod.common.client.net.animation.PlayPosableAnimationHandler
import com.cobblemon.mod.common.client.net.bag.UpdateBagMenuDataHandler
import com.cobblemon.mod.common.client.net.bag.UseItemOnPokemonFromBagHandler
import com.cobblemon.mod.common.client.net.battle.*
import com.cobblemon.mod.common.client.net.callback.move.OpenMoveCallbackHandler
import com.cobblemon.mod.common.client.net.callback.party.OpenPartyCallbackHandler
@@ -76,6 +78,8 @@ import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
import com.cobblemon.mod.common.net.messages.client.PlayerInteractOptionsPacket
import com.cobblemon.mod.common.net.messages.client.SetClientPlayerDataPacket
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.bag.UpdateBagMenuDataPacket
import com.cobblemon.mod.common.net.messages.client.bag.UseItemOnPokemonFromBagPacket
import com.cobblemon.mod.common.net.messages.client.battle.*
import com.cobblemon.mod.common.net.messages.client.callback.OpenMoveCallbackPacket
import com.cobblemon.mod.common.net.messages.client.callback.OpenPartyCallbackPacket
@@ -444,6 +448,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(BehaviourSyncPacket.ID, BehaviourSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
list.add(PacketRegisterInfo(BagCategoriesSyncPacket.ID, BagCategoriesSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
@@ -640,6 +645,10 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SetActiveTMPacket.ID, SetActiveTMPacket::decode, SetActiveTMPacketHandler))
list.add(PacketRegisterInfo(SetTMMachineContainerDataPacket.ID, SetTMMachineContainerDataPacket::decode, SetTMMachineContainerDataPacketHandler))
// Bag
list.add(PacketRegisterInfo(UpdateBagMenuDataPacket.ID, UpdateBagMenuDataPacket::decode, UpdateBagMenuDataHandler))
list.add(PacketRegisterInfo(UseItemOnPokemonFromBagPacket.ID, UseItemOnPokemonFromBagPacket::decode, UseItemOnPokemonFromBagHandler))
// Behaviour Packets
list.add(PacketRegisterInfo(SetEntityBehaviourPacket.ID, SetEntityBehaviourPacket::decode, SetEntityBehaviourHandler))
list.add(PacketRegisterInfo(DamageOnCollisionPacket.ID, DamageOnCollisionPacket::decode, DamageOnCollisionPacketHandler))
@@ -647,7 +656,7 @@ object CobblemonNetwork {
// Habitat blocks
list.add(PacketRegisterInfo(SaveHabitatBlockSettingsPacket.ID, SaveHabitatBlockSettingsPacket::decode, SaveHabitatBlockSettingsHandler))
// Debug / cheats
// Debug
list.add(PacketRegisterInfo(RequestOpenRidingStatsDebugGUIPacket.ID, RequestOpenRidingStatsDebugGUIPacket::decode, RequestOpenRidingStatsDebugGUIHandler))
@@ -321,6 +321,19 @@ object CobblemonSounds : PlatformRegistry<Registry<SoundEvent>, ResourceKey<Regi
@JvmField
val MEDICINAL_LEEK_PLACE = this.create("block.medicinal_leek.plant")
@JvmField
val ITEM_BAG_OPEN = this.create("item.bag.open")
@JvmField
val ITEM_BAG_CLOSE = this.create("item.bag.close")
@JvmField
val ITEM_BAG_CLICK = this.create("item.bag.click")
@JvmField
val ITEM_BAG_ITEM_IN = this.create("item.bag.item_in")
@JvmField
val ITEM_BAG_ITEM_OUT = this.create("item.bag.item_out")
@JvmField
val ITEM_BAG_EQUIP = this.create("item.bag.equip")
@JvmField
val GILDED_CHEST_OPEN = this.create("block.gilded_chest.open")
@JvmField
@@ -0,0 +1,66 @@
/*
* 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.bag
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.BagCategoriesSyncPacket
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.TagKeyAdapter
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
object BagCategories: JsonDataRegistry<BagCategory> {
override val id: ResourceLocation = cobblemonResource("bag_categories")
override val type: PackType = PackType.SERVER_DATA
override val observable = SimpleObservable<BagCategories>()
override val gson = GsonBuilder()
.disableHtmlEscaping()
.setPrettyPrinting()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(TypeToken.getParameterized(TagKey::class.java, Item::class.java).type, TagKeyAdapter(Registries.ITEM))
.create()
override val typeToken: TypeToken<BagCategory> = TypeToken.get(BagCategory::class.java)
override val resourcePath = "bag_categories"
private val bagCategories = hashMapOf<ResourceLocation, BagCategory>()
override fun reload(data: Map<ResourceLocation, BagCategory>) {
bagCategories.clear()
data.forEach { identifier, category ->
try {
category.identifier = identifier
bagCategories[identifier] = category
} catch (e: Exception) {
Cobblemon.LOGGER.error("Skipped loading the {} category", identifier, e)
}
}
Cobblemon.LOGGER.info("Loaded {} bag categories", bagCategories.size)
this.observable.emit(this)
}
override fun sync(player: ServerPlayer) {
BagCategoriesSyncPacket(bagCategories.values.toList())
}
fun getOrderedCategoriesList(): List<BagCategory> {
return bagCategories.values.toList().sortedBy { it.order }
}
}
@@ -0,0 +1,66 @@
/*
* 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.bag
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.writeIdentifier
import net.minecraft.core.registries.Registries
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.tags.TagKey
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
class BagCategory(
identifier: ResourceLocation,
val order: Int,
val icon: ResourceLocation,
val whiteListTag: TagKey<Item>,
val blackListTag: TagKey<Item>,
val maxCount: Int
): StringRepresentable {
@Transient
var identifier: ResourceLocation = identifier
internal set
override fun getSerializedName(): String = identifier.toString()
fun getName(): MutableComponent = Component.translatable("${identifier.namespace}.bag_category.${identifier.path}")
fun itemIsValid(itemStack: ItemStack): Boolean {
if (itemStack.`is`(blackListTag)) return false
if (itemStack.`is`(whiteListTag)) return true
return false
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeIdentifier(identifier)
buffer.writeInt(order)
buffer.writeIdentifier(icon)
buffer.writeIdentifier(whiteListTag.location)
buffer.writeIdentifier(blackListTag.location)
buffer.writeInt(maxCount)
}
companion object {
internal fun decode(buffer: RegistryFriendlyByteBuf): BagCategory {
return BagCategory(
buffer.readIdentifier(),
buffer.readInt(),
buffer.readIdentifier(),
TagKey.create(Registries.ITEM, buffer.readIdentifier()),
TagKey.create(Registries.ITEM, buffer.readIdentifier()),
buffer.readInt()
)
}
}
}
@@ -9,12 +9,14 @@
package com.cobblemon.mod.common.api.events.pokemon
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.api.events.Cancelable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeParticipant
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.getPlayer
/**
@@ -31,9 +33,9 @@ interface TradeEvent {
val tradeParticipant1: TradeParticipant
/**
* The [Pokemon] being traded by the first participant.
* All the [Pokemon] the first participant receives in the trade.
*/
val tradeParticipant1Pokemon: Pokemon
val tradeParticipant1Pokemon: List<Pokemon>
/**
* The second [TradeParticipant].
@@ -41,9 +43,19 @@ interface TradeEvent {
val tradeParticipant2: TradeParticipant
/**
* The [Pokemon] being traded by the second participant.
* All the [Pokemon] the second participant receives in the trade.
*/
val tradeParticipant2Pokemon: Pokemon
val tradeParticipant2Pokemon: List<Pokemon>
fun createContext(): MutableMap<String, MoValue> = mutableMapOf(
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
// Kept for backwards compatibility, this is the first Pokémon of each side.
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.first().struct,
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.first().struct,
"trade_participant_1_pokemon_list" to tradeParticipant1Pokemon.asArrayValue { it.struct },
"trade_participant_2_pokemon_list" to tradeParticipant2Pokemon.asArrayValue { it.struct }
)
/**
* Event fired when a trade is about to happen. Cancelling this event prevents the trade from occurring.
@@ -53,16 +65,11 @@ interface TradeEvent {
*/
data class Pre(
override val tradeParticipant1: TradeParticipant,
override val tradeParticipant1Pokemon: Pokemon,
override val tradeParticipant1Pokemon: List<Pokemon>,
override val tradeParticipant2: TradeParticipant,
override val tradeParticipant2Pokemon: Pokemon
override val tradeParticipant2Pokemon: List<Pokemon>
) : TradeEvent, Cancelable() {
val context = mutableMapOf(
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
)
val context = createContext()
val functions = moLangFunctionMap(
cancelFunc
@@ -77,15 +84,10 @@ interface TradeEvent {
*/
data class Post(
override val tradeParticipant1: TradeParticipant,
override val tradeParticipant1Pokemon: Pokemon,
override val tradeParticipant1Pokemon: List<Pokemon>,
override val tradeParticipant2: TradeParticipant,
override val tradeParticipant2Pokemon: Pokemon
override val tradeParticipant2Pokemon: List<Pokemon>
) : TradeEvent {
val context = mutableMapOf(
"trade_participant_1" to (tradeParticipant1.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_2" to (tradeParticipant2.uuid.getPlayer()?.asMoLangValue() ?: DoubleValue.ZERO),
"trade_participant_1_pokemon" to tradeParticipant1Pokemon.struct,
"trade_participant_2_pokemon" to tradeParticipant2Pokemon.struct
)
val context = createContext()
}
}
@@ -92,17 +92,13 @@ interface PokemonAndMoveSelectingItem {
}
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
if (pokemon.entity == null && stack.`is`(CobblemonItemTags.POKE_FOOD) && pokemon.isFull()) {
return false
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
return !pokemon.isFull()
}
return true
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean {
return bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
fun canUseOnMove(stack: ItemStack, pokemon: Pokemon, move: Move): Boolean = canUseOnMove(stack, move)
fun canUseOnMove(stack: ItemStack, move: Move): Boolean
@@ -107,16 +107,13 @@ interface PokemonSelectingItem {
}
fun canUseOnPokemon(stack: ItemStack, pokemon: Pokemon): Boolean {
if (pokemon.entity == null && stack.`is`(CobblemonItemTags.POKE_FOOD) && pokemon.isFull()) {
return false
if (stack.`is`(CobblemonItemTags.POKE_FOOD)) {
return !pokemon.isFull()
}
return true
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean {
return bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
}
fun canUseOnBattlePokemon(stack: ItemStack, battlePokemon: BattlePokemon): Boolean = bagItem!!.canUse(stack, battlePokemon.actor.battle, battlePokemon)
fun interactWithSpecificBattle(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon): InteractionResultHolder<ItemStack> {
return if (canUseOnBattlePokemon(stack, battlePokemon)) {
@@ -55,9 +55,8 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
}
functions["feed_pokemon"] = { params ->
val amount = params.getDouble(0)
val playSound = params.getBooleanOrNull(1) != false
val playMoodParticles = params.getBooleanOrNull(2) != false
DoubleValue(pokemon.feedPokemon(amount.toInt(), playSound, playMoodParticles))
val playSound = params.getBooleanOrNull(1) ?: true
pokemon.feedPokemon(amount.toInt(), playSound)
}
functions["behaviour"] = { pokemon.form.behaviour.struct }
functions["behavior"] = { pokemon.form.behaviour.struct } // Inferior
@@ -44,7 +44,6 @@ object ActionEffects : JsonDataRegistry<ActionEffectTimeline> {
ActionEffectKeyframe.register<EntityMoLangActionEffectKeyframe>("entity_molang")
ActionEffectKeyframe.register<MoLangActionEffectKeyframe>("molang")
ActionEffectKeyframe.register<ParallelActionEffectKeyframe>("parallel")
ActionEffectKeyframe.register<LoopActionEffectKeyframe>("loop")
ActionEffectKeyframe.register<CanInterruptActionEffectKeyframe>("can_interrupt")
ActionEffectKeyframe.register<CannotInterruptActionEffectKeyframe>("cannot_interrupt")
ActionEffectKeyframe.register<RemoveHoldsActionEffectKeyframe>("remove_holds")
@@ -1,39 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.api.moves.animations.keyframes
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import java.util.concurrent.CompletableFuture
class LoopActionEffectKeyframe(
val keyframe: ActionEffectKeyframe = PauseActionEffectKeyframe()
) : ConditionalActionEffectKeyframe() {
override fun playWhenTrue(context: ActionEffectContext): CompletableFuture<Unit> {
val future = CompletableFuture<Unit>()
runIteration(context, future)
return future
}
private fun runIteration(context: ActionEffectContext, future: CompletableFuture<Unit>) {
context.currentKeyframes.add(keyframe)
keyframe.play(context)
.thenRun { context.currentKeyframes.remove(keyframe) }
.thenAccept {
if (condition.resolveBoolean(context.runtime)) {
runIteration(context, future)
} else {
future.complete(Unit)
}
}
.exceptionally {
future.completeExceptionally(it)
null
}
}
}
@@ -140,6 +140,20 @@ object CobblemonItemTags {
@JvmField val WEARABLE_HAT_ITEMS = create("held/visibility/hat")
@JvmField val HIDDEN_ITEMS = create("held/visibility/hidden")
//Bag Categories Tags
@JvmField val BAG_WHITELIST_POKE_BALLS = create("bag/whitelist_poke_balls")
@JvmField val BAG_BLACKLIST_POKE_BALLS = create("bag/blacklist_poke_balls")
@JvmField val BAG_WHITELIST_CONSUMABLES = create("bag/whitelist_consumables")
@JvmField val BAG_BLACKLIST_CONSUMABLES = create("bag/blacklist_consumables")
@JvmField val BAG_WHITELIST_BERRIES = create("bag/whitelist_berries")
@JvmField val BAG_BLACKLIST_BERRIES = create("bag/blacklist_berries")
@JvmField val BAG_WHITELIST_HELD_EVO = create("bag/whitelist_held_evo")
@JvmField val BAG_BLACKLIST_HELD_EVO = create("bag/blacklist_held_evo")
@JvmField val BAG_WHITELIST_CURRENCY = create("bag/whitelist_currency")
@JvmField val BAG_BLACKLIST_CURRENCY = create("bag/blacklist_currency")
@JvmField val BAG_WHITELIST_TMS = create("bag/whitelist_tms")
@JvmField val BAG_BLACKLIST_TMS = create("bag/blacklist_tms")
//Empty Tag
@JvmField val EMPTY = create("empty")
@@ -24,6 +24,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.RandomSource
import net.minecraft.world.InteractionResult
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
@@ -31,13 +32,9 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.TooltipFlag
import net.minecraft.world.item.context.BlockPlaceContext
import net.minecraft.world.level.BlockGetter
import net.minecraft.world.level.CollisionGetter
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.BaseEntityBlock
import net.minecraft.world.level.block.Block
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.Mirror
import net.minecraft.world.level.block.RenderShape
import net.minecraft.world.level.block.Rotation
import net.minecraft.world.level.block.*
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.entity.BlockEntityType
@@ -48,9 +45,11 @@ import net.minecraft.world.level.block.state.properties.IntegerProperty
import net.minecraft.world.level.block.state.properties.Property
import net.minecraft.world.level.pathfinder.PathComputationType
import net.minecraft.world.phys.BlockHitResult
import net.minecraft.world.phys.Vec3
import net.minecraft.world.phys.shapes.CollisionContext
import net.minecraft.world.phys.shapes.Shapes
import net.minecraft.world.phys.shapes.VoxelShape
import java.util.Optional
@Suppress("DEPRECATED", "OVERRIDE_DEPRECATION")
class HealingMachineBlock(settings: Properties) : BaseEntityBlock(settings) {
@@ -79,6 +78,14 @@ class HealingMachineBlock(settings: Properties) : BaseEntityBlock(settings) {
const val MAX_CHARGE_LEVEL = 5
val CHARGE_LEVEL: IntegerProperty = IntegerProperty.create("charge", 0, MAX_CHARGE_LEVEL + 1)
val NATURAL: BooleanProperty = BooleanProperty.create("natural")
fun canSetSpawn(level: Level): Boolean {
return level.gameRules.getBoolean(CobblemonGameRules.HEALER_SET_SPAWN)
}
fun findStandUpPosition(entityType: EntityType<*>, level: CollisionGetter, pos: BlockPos): Optional<Vec3> {
return RespawnAnchorBlock.findStandUpPosition(entityType, level, pos) // Exists if we ever want to make something more custom, but anchor block logic should suffice
}
}
init {
@@ -158,7 +165,12 @@ class HealingMachineBlock(settings: Properties) : BaseEntityBlock(settings) {
}
val party = serverPlayerEntity.party()
val pc = serverPlayerEntity.pc()
val healPC = player.level().gameRules.getBoolean(CobblemonGameRules.HEALERS_HEAL_PC);
val healPC = player.level().gameRules.getBoolean(CobblemonGameRules.HEALERS_HEAL_PC)
// Do spawnpoint stuff
if (canSetSpawn(world)) {
player.setRespawnPosition(world.dimension(), blockPos, player.yRot, false, true)
}
if (party.none() && (!healPC || pc.none())) {
player.sendSystemMessage(lang("healingmachine.nopokemon").red(), true)
@@ -183,6 +195,7 @@ class HealingMachineBlock(settings: Properties) : BaseEntityBlock(settings) {
player.sendSystemMessage(lang("healingmachine.notenoughcharge", "${((neededCharge/party.count())*100f).toInt()}%").red(), true)
}
party.forEach { it.tryRecallWithAnimation() }
return InteractionResult.CONSUME
}
@@ -160,8 +160,8 @@ class TypeGemClusterBlock(
&& state.getValue(STAGE) == 3) return false
val supportPos = pos.relative(state.getValue(FACING).opposite)
val supportBlock = level.getBlockState(supportPos).block
return CobblemonBlocks.typeGemBlocks().containsValue(supportBlock) || (supportBlock == CobblemonBlocks.TYPE_GEM_CORE) // Cannot grow if not on any gem blocks
val supportState = level.getBlockState(supportPos)
return supportState.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS) || supportState.block == CobblemonBlocks.TYPE_GEM_CORE // Cannot grow if not on any gem blocks
}
@@ -51,10 +51,20 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
ResourceLocation.parse("cobblemon:dragon_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_DRAGON,
ResourceLocation.parse("cobblemon:dark_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_DARK,
ResourceLocation.parse("cobblemon:steel_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_STEEL,
ResourceLocation.parse("cobblemon:fairy_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY
ResourceLocation.parse("cobblemon:fairy_gem_block") to CobblemonBlocks.TYPE_GEM_CLUSTER_FAIRY,
ResourceLocation.parse("minecraft:amethyst_block") to Blocks.SMALL_AMETHYST_BUD
)
}
val AMETHYST_STAGES = listOf(
Blocks.SMALL_AMETHYST_BUD,
Blocks.MEDIUM_AMETHYST_BUD,
Blocks.LARGE_AMETHYST_BUD,
Blocks.AMETHYST_CLUSTER
)
private fun isAmethystStage(block: Block) = block in AMETHYST_STAGES
fun getConnectedGemBlocks(level: BlockGetter, pos: BlockPos): List<Pair<BlockState, BlockPos>> {
val connectedGems = mutableListOf<Pair<BlockState, BlockPos>>()
val visited = mutableSetOf<BlockPos>()
@@ -85,12 +95,16 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
Direction.entries.forEach {
if (random.nextFloat() < Cobblemon.config.typeGemGrowthChance) {
val faceState = level.getBlockState(pos.relative(it))
val randomGrowthType = decideClusterGrowthType(level, pos)
val newState = BLOCK_TO_CLUSTER[BuiltInRegistries.BLOCK.getKey(state.block)]?.defaultBlockState()
var newState = BLOCK_TO_CLUSTER[BuiltInRegistries.BLOCK.getKey(state.block)]?.defaultBlockState()
?.setValue(DirectionalBlock.FACING, it)
?.setValue(GROWTH_TYPE, randomGrowthType)
?: return
if (newState.hasProperty(GROWTH_TYPE)) {
newState = newState.setValue(GROWTH_TYPE, decideClusterGrowthType(level, pos))
} else if (getConnectedGemBlocks(level, pos).size > Cobblemon.config.typeGemMaxConnected) {
return@forEach
}
if (faceState.`is`(Blocks.AIR)) {
level.setBlock(pos.relative(it), newState, 3)
}
@@ -132,7 +146,7 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
if (!visited.add(clusterPos)) continue
val clusterState = level.getBlockState(clusterPos)
if (clusterState.block == expectedCluster) {
if (clusterState.block == expectedCluster && clusterState.hasProperty(GROWTH_TYPE)) {
clusters += clusterState to clusterPos
}
}
@@ -178,13 +192,41 @@ class TypeGemCoreBlock(properties: Properties) : Block(properties) {
Direction.entries.forEach { direction ->
val relativePos = pos.relative(direction)
val relativeState = level.getBlockState(relativePos)
val clusterBlock = relativeState.block as? TypeGemClusterBlock ?: return@forEach
if ((relativeState.getValue(DirectionalBlock.FACING) == direction &&
random.nextFloat() < Cobblemon.config.typeGemGrowthChance) || forceGrow
) {
clusterBlock.advanceGrowth(relativeState, level, relativePos, random)
val matchesFace = relativeState.hasProperty(DirectionalBlock.FACING)
&& relativeState.getValue(DirectionalBlock.FACING) == direction
if (!(forceGrow || (matchesFace && random.nextFloat() < Cobblemon.config.typeGemGrowthChance))) {
return@forEach
}
val block = relativeState.block
if (block is TypeGemClusterBlock) {
block.advanceGrowth(relativeState, level, relativePos, random)
} else if (isAmethystStage(block)) {
advanceAmethyst(relativeState, level, relativePos)
}
}
}
private fun advanceAmethyst(state: BlockState, level: LevelAccessor, pos: BlockPos) {
val facing = state.getValue(DirectionalBlock.FACING)
val supportState = level.getBlockState(pos.relative(facing.opposite))
if (!supportState.`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS) && supportState.block != CobblemonBlocks.TYPE_GEM_CORE) {
return
}
if (state.block != Blocks.AMETHYST_CLUSTER) {
val nextStage = AMETHYST_STAGES[AMETHYST_STAGES.indexOf(state.block) + 1]
level.setBlock(pos, nextStage.defaultBlockState().setValue(DirectionalBlock.FACING, facing), 3)
return
}
// Fully grown clusters mature into amethyst blocks unless boxed in, mirroring stunted type gems
val boxedIn = Direction.entries.any { direction ->
direction != facing.opposite
&& level.getBlockState(pos.relative(direction)).`is`(CobblemonBlockTags.TYPE_GEM_BLOCKS)
}
if (!boxedIn) {
level.setBlock(pos, Blocks.AMETHYST_BLOCK.defaultBlockState(), 3)
}
}
@@ -9,10 +9,13 @@
package com.cobblemon.mod.common.client
import com.cobblemon.mod.common.BakingOverride
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.item.bag.CobblemonBagItem
import com.cobblemon.mod.common.util.cobblemonModel
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.resources.model.ModelResourceLocation
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item
/**
* The purpose of this class is to hold models that we want baked, but aren't associated with
@@ -103,9 +106,33 @@ object CobblemonBakingOverrides {
cobblemonModel("surprise_mulch", "none")
)
// Bag
val bagModels = mutableMapOf<CobblemonBagItem, BakingOverride>()
val TRAINER_PACK = registerBagOverride(
CobblemonItems.TRAINER_PACK,
cobblemonResource("item/bag/trainer_pack"),
cobblemonModel("trainer_pack", "none")
)
val TRAINER_BAG = registerBagOverride(
CobblemonItems.TRAINER_BAG,
cobblemonResource("item/bag/trainer_bag"),
cobblemonModel("trainer_bag", "none")
)
/*val BAG_PENNY = registerBagOverride(
CobblemonItems.TRAINER_PACK,
cobblemonResource("item/bag/bag_penny"),
cobblemonModel("bag_penny", "penny")
)*/
fun registerOverride(modelLocation: ResourceLocation, modelIdentifier: ModelResourceLocation): BakingOverride {
val result = BakingOverride(modelLocation, modelIdentifier)
models.add(result)
return result
}
fun registerBagOverride(associatedItem: CobblemonBagItem, modelLocation: ResourceLocation, modelIdentifier: ModelResourceLocation): BakingOverride {
val result = registerOverride(modelLocation, modelIdentifier)
bagModels[associatedItem] = result
return result
}
}
@@ -27,6 +27,7 @@ import com.cobblemon.mod.common.client.battle.ClientBattle
import com.cobblemon.mod.common.client.gui.PartyOverlay
import com.cobblemon.mod.common.client.gui.PartyOverlayDataControl
import com.cobblemon.mod.common.client.gui.RideControlsOverlay
import com.cobblemon.mod.common.client.gui.bag.CobblemonBagScreen
import com.cobblemon.mod.common.client.gui.battle.BattleOverlay
import com.cobblemon.mod.common.client.gui.cookingpot.CookingPotScreen
import com.cobblemon.mod.common.client.gui.party.PartyTutorialToasts
@@ -46,6 +47,7 @@ import com.cobblemon.mod.common.client.render.generic.GenericBedrockRenderer
import com.cobblemon.mod.common.client.render.item.CobblemonBuiltinItemRendererRegistry
import com.cobblemon.mod.common.client.render.item.PokemonItemRenderer
import com.cobblemon.mod.common.client.render.layer.PlayerRecallBallRenderer
import com.cobblemon.mod.common.client.render.layer.CobblemonBagRenderer
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
@@ -391,7 +393,7 @@ object CobblemonClient {
CobblemonBlocks.X_SP_ATK,
CobblemonBlocks.X_SP_DEF,
CobblemonBlocks.X_SPEED,
CobblemonBlocks.TM_MACHINE,
CobblemonBlocks.TM_MACHINE,
CobblemonBlocks.GALARICA_CUFF,
CobblemonBlocks.GALARICA_WREATH,
@@ -419,7 +421,7 @@ object CobblemonClient {
CobblemonBlocks.MOOMOO_MILK,
CobblemonBlocks.MEDICINAL_BREW,
CobblemonBlocks.TERRAIN_EXTENDER,
CobblemonBlocks.RED_MINT_LEAF_LITTER,
CobblemonBlocks.POTTED_RED_MINT,
CobblemonBlocks.BLUE_MINT_LEAF_LITTER,
@@ -539,14 +541,17 @@ object CobblemonClient {
skinMap?.get(PlayerSkin.Model.WIDE) as LivingEntityRenderer<Player, PlayerModel<Player>>
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
renderer?.addLayer(CobblemonBagRenderer(renderer))
renderer = skinMap[PlayerSkin.Model.SLIM] as LivingEntityRenderer<Player, PlayerModel<Player>>?
renderer?.addLayer(PokemonOnShoulderRenderer(renderer))
renderer?.addLayer(PlayerRecallBallRenderer(renderer))
renderer?.addLayer(CobblemonBagRenderer(renderer))
}
private fun registerMenuScreens() {
MenuScreens.register(CobblemonMenuType.COOKING_POT, ::CookingPotScreen)
MenuScreens.register(CobblemonMenuType.TM_MACHINE, ::TMMachineScreen)
MenuScreens.register(CobblemonMenuType.COBBLEMON_BAG, ::CobblemonBagScreen)
}
private fun registerBlockEntityRenderers() {
@@ -0,0 +1,88 @@
/*
* 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.bag
import com.cobblemon.mod.common.api.bag.BagCategories
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
class BagCategoryButton(
x: Int,
y: Int,
val parent: CobblemonBagScreen,
val category: Int,
val action: OnPress
): Button(x, y, WIDTH, HEIGHT, Component.empty(), action, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private const val WIDTH = 32
private const val HEIGHT = 31
private val CATEGORY_SLOT = cobblemonResource("textures/gui/bag/bag_tab.png")
private val VOLUME_BAR = cobblemonResource("textures/gui/bag/bag_tab_volume_bar.png")
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
val orderedCategories = BagCategories.getOrderedCategoriesList()
blitk(
matrixStack = context.pose(),
texture = CATEGORY_SLOT,
x = x, y = y,
width = WIDTH, height = HEIGHT,
textureHeight = 64,
vOffset = if (parent.menu.currentlySelectedCategory == category) 32 else 0
)
blitk(
matrixStack = context.pose(),
texture = if (category == -1) ResourceLocation("minecraft", "textures/item/bundle.png") else orderedCategories[category].icon,
x = x + 8, y = y + 9,
width = 16, height = 16
)
//The Bar
if (category != -1) {
blitk(
matrixStack = context.pose(),
texture = VOLUME_BAR,
x = x + 8, y = y + 25,
width = 16, height = 3
)
val itemRatio = (parent.menu.categoryItemCounts[category].toFloat() / orderedCategories[category].maxCount).coerceIn(0F, 1F)
val barWidth = itemRatio * 14
val (red, green) = getDepletableRedGreen(1 - itemRatio)
blitk(
matrixStack = context.pose(),
texture = CobblemonResources.WHITE,
x = x + 9, y = y + 26,
width = barWidth, height = 1,
red = red * 0.8F, green = green * 0.8F, blue = 0.27F
)
}
if (isHovered) {
context.renderTooltip(Minecraft.getInstance().font, if (category == -1) lang("bag_categories.all_items") else orderedCategories[category].getName(), mouseX, mouseY)
}
}
override fun playDownSound(handler: SoundManager) {}
}
@@ -0,0 +1,50 @@
/*
* 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.bag
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
class BagCategoryPageButton(
x: Int,
y: Int,
val parent: CobblemonBagScreen,
val leftButton: Boolean,
val action: OnPress
): Button(x, y, WIDTH, HEIGHT, Component.empty(), action, DEFAULT_NARRATION), CobblemonRenderable {
companion object {
private const val WIDTH = 12
private const val HEIGHT = 10
private val LEFT_BUTTON = cobblemonResource("textures/gui/bag/button_left.png")
private val RIGHT_BUTTON = cobblemonResource("textures/gui/bag/button_right.png")
}
var enabled = true
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
blitk(
matrixStack = context.pose(),
texture = if (leftButton) LEFT_BUTTON else RIGHT_BUTTON,
x = x / 0.5F, y = y / 0.5F,
width = WIDTH * 2, height = HEIGHT * 2,
textureHeight = 40,
vOffset = if (enabled) 20 else 0,
scale = 0.5f
)
}
override fun playDownSound(handler: SoundManager) {}
}
@@ -0,0 +1,416 @@
/*
* 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.bag
import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.bag.BagCategories
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.client.render.getDepletableRedGreen
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
import com.cobblemon.mod.common.item.battle.BagItem
import com.cobblemon.mod.common.net.messages.client.bag.UpdateBagMenuDataPacket
import com.cobblemon.mod.common.net.messages.client.bag.UseItemOnPokemonFromBagPacket
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.fromEulerXYZDegrees
import juuxel.adorn.util.Colors
import net.fabricmc.api.EnvType
import net.fabricmc.api.Environment
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.inventory.ClickType
import net.minecraft.world.inventory.Slot
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.min
import kotlin.math.roundToInt
@Environment(EnvType.CLIENT)
class CobblemonBagScreen(
menu: CobblemonBagMenu,
playerInventory: Inventory,
title: Component
): AbstractContainerScreen<CobblemonBagMenu>(
menu,
playerInventory,
Component.translatable("cobblemon.container.bag")
) {
companion object {
private const val BACKGROUND_HEIGHT = 197
private const val BACKGROUND_WIDTH = 184
private val BACKGROUND = cobblemonResource("textures/gui/bag/bag_base.png")
private val SCROLL_TRACK = cobblemonResource("textures/gui/bag/scrollbar_track.png")
private val SCROLL_SLIDE = cobblemonResource("textures/gui/bag/scrollbar_slide.png")
private val PARTY_SLOT_BASE = cobblemonResource("textures/gui/bag/party_slot_base.png")
private val PARTY_SLOT_COLLAPSED = cobblemonResource("textures/gui/bag/party_slot_collapsed.png")
private val PARTY_SLOT = cobblemonResource("textures/gui/bag/party_slot.png")
private val PARTY_SLOT_FAINTED = cobblemonResource("textures/gui/bag/party_slot_fainted.png")
private val GENDER_ICON_MALE = cobblemonResource("textures/gui/party/party_gender_male.png")
private val GENDER_ICON_FEMALE = cobblemonResource("textures/gui/party/party_gender_female.png")
}
val categoriesWidgets = mutableListOf<BagCategoryButton>()
lateinit var allItemsCategoryWidget: BagCategoryButton
lateinit var leftCategoryPageButton: BagCategoryPageButton
lateinit var rightCategoryPageButton: BagCategoryPageButton
var currentCategoryPage = 0
var lastCategory = menu.currentlySelectedCategory
var lastScroll = menu.scroll
var scrolling = false
init {
this.imageWidth = BACKGROUND_WIDTH + 69
this.imageHeight = BACKGROUND_HEIGHT + 27
}
override fun init() {
super.init()
this.leftPos -= 34
for (i in 0..3) {
if (i < BagCategories.getOrderedCategoriesList().size) {
categoriesWidgets.add(
BagCategoryButton(leftPos + 76 + i * 33, topPos, this, i) {
if (menu.currentlySelectedCategory != i) {
menu.changeCategory(i)
playSound(CobblemonSounds.ITEM_BAG_CLICK)
}
}.also { addRenderableWidget(it) }
)
} else break
}
allItemsCategoryWidget = BagCategoryButton(leftPos + 214, topPos, this, -1) {
if (menu.currentlySelectedCategory != -1) {
menu.changeCategory(-1)
playSound(CobblemonSounds.ITEM_BAG_CLICK)
}
}.also { addRenderableWidget(it) }
leftCategoryPageButton = BagCategoryPageButton(leftPos + 218, topPos + 32, this, true) {
if (leftCategoryPageButton.enabled) {
cycleCategoryPage(false)
playSound(CobblemonSounds.ITEM_BAG_CLICK)
}
}.also { addRenderableWidget(it) }
rightCategoryPageButton = BagCategoryPageButton(leftPos + 229, topPos + 32, this, false) {
if (rightCategoryPageButton.enabled) {
cycleCategoryPage(true)
playSound(CobblemonSounds.ITEM_BAG_CLICK)
}
}.also { addRenderableWidget(it) }
playSound(CobblemonSounds.ITEM_BAG_OPEN)
}
override fun renderBg(context: GuiGraphics, partialTick: Float, mouseX: Int, mouseY: Int) {}
override fun renderBackground(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {}
override fun renderLabels(guiGraphics: GuiGraphics, mouseX: Int, mouseY: Int) {}
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, partialTick: Float) {
renderTransparentBackground(context)
val matrices = context.pose()
// Background
blitk(
matrixStack = matrices,
texture = BACKGROUND,
x = leftPos + 69, y = topPos + 27,
width = BACKGROUND_WIDTH, height = BACKGROUND_HEIGHT
)
//Scroll
val scrollYOffset = if (menu.maxScroll == 0) {
if (menu.scroll == 0) 0 else 53
} else ((min(menu.scroll, menu.maxScroll).toFloat() / menu.maxScroll) * 53).toInt()
val trackHeight = 71 - scrollYOffset - 13
if (trackHeight > 0) {
blitk(
matrixStack = matrices,
texture = SCROLL_TRACK,
x = leftPos + 230, y = topPos + 49 + (71 - trackHeight),
width = 9, height = trackHeight,
textureHeight = 71,
vOffset = 71 - trackHeight
)
}
blitk(
matrixStack = matrices,
texture = SCROLL_SLIDE,
x = leftPos + 230, y = topPos + 49 + scrollYOffset,
width = 9, height = 17
)
// Pokemon
val party = CobblemonClient.storage.party
for (i in 0..5) {
val pokemon = party.get(i)
val slotYPosition = topPos + i * 33 + 27
if (pokemon != null) {
blitk(
matrixStack = matrices,
texture = PARTY_SLOT_BASE,
x = leftPos, y = slotYPosition,
width = 67, height = 31
)
val slotWidth = 32
val slotLeft = leftPos + 11
blitk(
matrixStack = matrices,
texture = if (pokemon.isFainted()) PARTY_SLOT_FAINTED else PARTY_SLOT,
x = slotLeft, y = slotYPosition,
width = slotWidth, height = 26,
textureHeight = 52,
vOffset = if (!menu.carried.isEmpty && menu.carried.item is BagItem && partySlotHovered(i, mouseX, mouseY)) 26 else 0
)
val expForThisLevel = pokemon.experience - pokemon.experienceGroup.getExperience(pokemon.level)
val expToNextLevel = pokemon.experienceGroup.getExperience(pokemon.level + 1) - pokemon.experienceGroup.getExperience(pokemon.level)
val expRatio = expForThisLevel.toFloat() / expToNextLevel
val expBarHeight = expRatio * 20
if (expBarHeight > 0) {
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = leftPos + 6, y = slotYPosition + 24 - expBarHeight,
width = 1, height = expBarHeight,
red = 0.2, green = 0.65, blue = 0.84
)
}
val hpRatio = pokemon.currentHealth.toFloat() / pokemon.maxHealth
val hpBarHeight = hpRatio * 20
val (red, green) = getDepletableRedGreen(hpRatio)
if (hpBarHeight > 0) {
blitk(
matrixStack = matrices,
texture = CobblemonResources.WHITE,
x = leftPos + 8, y = slotYPosition + 24 - hpBarHeight,
width = 2, height = hpBarHeight,
red = red * 0.8F, green = green * 0.8F, blue = 0.27F
)
}
// Ball Icon
val ballIcon = cobblemonResource("textures/gui/ball/" + pokemon.caughtBall.name.path + ".png")
blitk(
matrixStack = matrices,
texture = ballIcon,
x = (leftPos + 3.5F) / 0.5F, y = (slotYPosition + 23) / 0.5F,
width = 18, height = 22,
textureHeight = 44,
scale = 0.5F
)
//Gender
if (pokemon.gender != Gender.GENDERLESS) {
blitk(
matrixStack = matrices,
texture = if (pokemon.gender == Gender.MALE) GENDER_ICON_MALE else GENDER_ICON_FEMALE,
x = (leftPos + 62) / 0.5F, y = (slotYPosition + 26) / 0.5F,
width = 5, height = 7,
scale = 0.5F
)
}
//Status
val status = pokemon.status?.status
if (!pokemon.isFainted() && status != null) {
blitk(
matrixStack = matrices,
texture = cobblemonResource("textures/gui/bag/status_${status.showdownName}.png"),
x = leftPos + 1, y = slotYPosition + 9,
width = 4, height = 14
)
}
matrices.pushPose()
matrices.translate(slotLeft + ((slotWidth - 8) / 2.0), slotYPosition.toDouble() - 6, 0.0)
matrices.scale(3.5F, 3.5F, 1F)
val state = FloatingState()
state.currentAspects = pokemon.aspects
drawProfilePokemon(
species = pokemon.species.resourceIdentifier,
matrixStack = matrices,
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
state = state,
scale = 4.5F,
partialTicks = 0F
)
matrices.popPose()
matrices.pushPose()
matrices.translate(0.0, 0.0, 100.0)
// Level and Name and Gender
drawScaledText(
context = context,
text = pokemon.getDisplayName(),
x = leftPos + 14, y = slotYPosition + 26,
scale = 0.5F
)
drawScaledTextJustifiedRight(
context = context,
text = lang("ui.lv"),
x = leftPos + 40, y = slotYPosition + 14,
shadow = true,
scale = 0.5F
)
drawScaledTextJustifiedRight(
context = context,
text = pokemon.level.toString().text(),
x = leftPos + 40, y = slotYPosition + 19,
shadow = true,
scale = 0.5F
)
matrices.popPose()
} else {
blitk(
matrixStack = matrices,
texture = PARTY_SLOT_COLLAPSED,
x = leftPos + 64, y = slotYPosition,
width = 5, height = 31
)
}
}
//Titles
context.drawString(font, title, leftPos + 80, topPos + 33, Colors.WHITE, false)
context.drawString(font, playerInventoryTitle, leftPos + 80, topPos + 129, 4210752, false)
super.render(context, mouseX, mouseY, partialTick)
renderTooltip(context, mouseX, mouseY)
}
fun partySlotHovered(index: Int, mouseX: Int, mouseY: Int): Boolean {
val left = leftPos + 11
val top = topPos + index * 33 + 27
return mouseX in left..(left + 32) && mouseY in top..(top + 26)
}
fun cycleCategoryPage(next: Boolean) {
val orderedCategories = BagCategories.getOrderedCategoriesList()
currentCategoryPage = (currentCategoryPage + if (next) 1 else -1).coerceIn(0, (orderedCategories.size - 1) / 4)
categoriesWidgets.forEach {
removeWidget(it)
}
categoriesWidgets.clear()
for (i in 0..3) {
val adjustedIndex = i + 4 * currentCategoryPage
if (adjustedIndex < orderedCategories.size) {
categoriesWidgets.add(
BagCategoryButton(leftPos + 76 + i * 33, topPos, this, adjustedIndex) {
playSound(CobblemonSounds.ITEM_BAG_CLICK)
menu.changeCategory(adjustedIndex)
}.also { addRenderableWidget(it) }
)
} else break
}
leftCategoryPageButton.enabled = currentCategoryPage > 0
rightCategoryPageButton.enabled = currentCategoryPage < (orderedCategories.size - 1) / 4
}
fun updateScrollValue(mouseY: Double) {
val adjustedMouseY = mouseY - topPos - 49.0
if (adjustedMouseY < 0) {
menu.scroll = 0
} else if (adjustedMouseY > 71) {
menu.scroll = menu.maxScroll
} else {
menu.scroll = ((adjustedMouseY / 71.0) * (menu.maxScroll)).roundToInt()
}
menu.setupSlotsBasedOnContent()
}
override fun slotClicked(slot: Slot?, slotId: Int, mouseButton: Int, type: ClickType) {
if (slotId in CobblemonBagMenu.BAG_SLOTS && (lastCategory != menu.currentlySelectedCategory || lastScroll != menu.scroll)) {
lastCategory = menu.currentlySelectedCategory
lastScroll = menu.scroll
sendToServer(UpdateBagMenuDataPacket(menu.currentlySelectedCategory, menu.scroll)) // Synchronize this data
}
super.slotClicked(slot, slotId, mouseButton, type)
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
if (scrollY < 0 && menu.scroll < menu.maxScroll) {
menu.scroll += 1
} else if (scrollY > 0 && menu.scroll > 0) {
menu.scroll -= 1
}
menu.setupSlotsBasedOnContent()
return true
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
if (mouseX in (leftPos + 229.0)..(leftPos + 240.0) && mouseY in (topPos + 49.0)..(topPos + 120.0)) {
scrolling = true
updateScrollValue(mouseY)
return true
}
val party = CobblemonClient.storage.party
if (!menu.carried.isEmpty && menu.carried.item is BagItem) {
for (i in 0..5) {
if (party.get(i) != null && partySlotHovered(i, mouseX.toInt(), mouseY.toInt())) {
playSound(CobblemonSounds.GUI_CLICK)
sendToServer(UseItemOnPokemonFromBagPacket(i))
return true
}
}
}
return super.mouseClicked(mouseX, mouseY, button)
}
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
scrolling = false
return super.mouseReleased(mouseX, mouseY, button)
}
override fun mouseDragged(mouseX: Double, mouseY: Double, button: Int, dragX: Double, dragY: Double): Boolean {
if (scrolling) {
updateScrollValue(mouseY)
return true
}
return super.mouseDragged(mouseX, mouseY, button, dragX, dragY)
}
fun playSound(soundEvent: SoundEvent) {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
}
override fun onClose() {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.ITEM_BAG_CLOSE, 1.0F))
super.onClose()
}
}
@@ -28,9 +28,9 @@ import org.joml.Vector3f
open class PartySlot(
x: Int, y: Int,
private val pokemon: TradeStartedPacket.TradeablePokemon?,
val pokemon: TradeStartedPacket.TradeablePokemon?,
private val parent: TradeGUI,
private val isOpposing: Boolean = false,
val isOpposing: Boolean = false,
onPress: OnPress
) : Button(x, y, SIZE, SIZE, Component.literal("PartySlot"), onPress, DEFAULT_NARRATION), CobblemonRenderable {
val state = FloatingState()
@@ -42,6 +42,8 @@ open class PartySlot(
private val genderIconFemale = cobblemonResource("textures/gui/pc/gender_icon_female.png")
private val selectPointerResource = cobblemonResource("textures/gui/pc/pc_pointer.png")
private val untradeableResource = cobblemonResource("textures/gui/trade/trade_slot_icon_locked.png")
private val tradePartySlotSelectedResource = cobblemonResource("textures/gui/trade/trade_party_slot_selected.png")
private val tradePartySlotSelectedOpposingResource = cobblemonResource("textures/gui/trade/trade_party_slot_selected_opposing.png")
}
override fun playDownSound(soundManager: SoundManager) {
@@ -60,6 +62,17 @@ open class PartySlot(
)
}
if (hasSelected()) {
blitk(
matrixStack = matrices,
texture = if (isOpposing) tradePartySlotSelectedOpposingResource else tradePartySlotSelectedResource,
x = x - 2,
y = y - 2,
width = 29,
height = 29
)
}
if (pokemon != null) {
// context.enableScissor(x - 2, y + 2, x + SIZE + 4, y + SIZE + 4)
// Render Pokémon
@@ -118,7 +131,16 @@ open class PartySlot(
}
matrices.popPose()
if (hasSelected()) {
val isViewed = if (isOpposing) {
parent.opposingOfferedPokemon?.uuid == pokemon.pokemonId
} else {
parent.offeredPokemon?.uuid == pokemon.pokemonId
}
if (isViewed && pokemon.tradeable) {
matrices.pushPose()
matrices.translate(0.0, 0.0, 200.0)
blitk(
matrixStack = matrices,
texture = selectPointerResource,
@@ -128,6 +150,7 @@ open class PartySlot(
height = 8,
scale = TradeGUI.SCALE
)
matrices.popPose()
}
// Held Item
@@ -145,8 +168,9 @@ open class PartySlot(
}
open fun hasSelected(): Boolean {
val offeredPokemon = if (isOpposing) parent.trade.oppositeOffer.get() else parent.trade.myOffer.get()
return pokemon?.pokemonId == offeredPokemon?.uuid && pokemon != null
if (pokemon == null) return false
val offeredPokemons = if (isOpposing) parent.trade.oppositeOffer.get() else parent.trade.myOffer.get()
return offeredPokemons.any { it.uuid == pokemon.pokemonId }
}
fun isHovered(mouseX: Int, mouseY: Int) = pokemon?.tradeable != false && mouseX.toFloat() in (x.toFloat()..(x.toFloat() + SIZE)) && mouseY.toFloat() in (y.toFloat()..(y.toFloat() + SIZE))
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.client.gui.summary.widgets.common.reformatNature
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.drawScaledTextJustifiedRight
import com.cobblemon.mod.common.client.trade.ClientTrade
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeStartedPacket.TradeablePokemon
import com.cobblemon.mod.common.net.messages.server.trade.*
import com.cobblemon.mod.common.pokemon.Gender
@@ -98,15 +99,23 @@ class TradeGUI(
private val tradeAnimationArrowsResource = cobblemonResource("textures/gui/trade/trade_animation_arrows.png")
}
private var offeredPokemonModel: ModelWidget? = null
private var opposingOfferedPokemonModel: ModelWidget? = null
private var offeredPokemonModels: MutableList<ModelWidget> = mutableListOf()
private var opposingOfferedPokemonModels: MutableList<ModelWidget> = mutableListOf()
private lateinit var offeredPokemonMarkings: MarkingsWidget
private lateinit var opposingOfferedPokemonMarkings: MarkingsWidget
var selectedPartyIndices = mutableListOf<Int>()
var offeredPokemons: MutableList<Pokemon> = mutableListOf()
var opposingPokemons: MutableList<Pokemon> = mutableListOf()
// For backwards compatibility where single is referenced in rendering text info
var offeredPokemon: Pokemon? = null
var opposingOfferedPokemon: Pokemon? = null
var myGridMode = true
var opposingGridMode = true
var ticksElapsed = 0
var protectiveTicks = 0
var selectPointerOffsetY = 0
@@ -125,30 +134,67 @@ class TradeGUI(
}
trade.completedEmitter.subscribe {
val (pokemonId1, pokemonId2) = it
val myTradedPokemon = party.find { it?.pokemonId == pokemonId1 }
val theirTradedPokemon = traderParty.find { it?.pokemonId == pokemonId2 }
if (myTradedPokemon == null || theirTradedPokemon == null) {
val (pokemonIds1, pokemonIds2) = it
val myTradedPokemons = pokemonIds1.mapNotNull { id -> party.find { it?.pokemonId == id } }
val theirTradedPokemons = pokemonIds2.mapNotNull { id -> traderParty.find { it?.pokemonId == id } }
if (myTradedPokemons.isEmpty() || theirTradedPokemons.isEmpty()) {
CobblemonNetwork.sendToServer(CancelTradePacket())
return@subscribe onClose()
}
val tradedSlot = party.indexOf(myTradedPokemon)
val opposingTradedSlot = traderParty.indexOf(theirTradedPokemon)
party[tradedSlot] = theirTradedPokemon
traderParty[opposingTradedSlot] = myTradedPokemon
myTradedPokemons.forEach { pk ->
val idx = party.indexOf(pk)
if (idx != -1) party[idx] = null
}
theirTradedPokemons.forEach { pk ->
val idx = traderParty.indexOf(pk)
if (idx != -1) traderParty[idx] = null
}
val newSelectedIndices = mutableListOf<Int>()
theirTradedPokemons.forEach { pk ->
val emptyIdx = party.indexOf(null)
if (emptyIdx != -1) {
party[emptyIdx] = pk
}
}
myTradedPokemons.forEach { pk ->
val emptyIdx = traderParty.indexOf(null)
if (emptyIdx != -1) traderParty[emptyIdx] = pk
}
val compactUI = { list: MutableList<TradeStartedPacket.TradeablePokemon?> ->
val nonNulls = list.filterNotNull()
list.clear()
list.addAll(nonNulls)
while (list.size < 6) list.add(null)
}
compactUI(party)
compactUI(traderParty)
theirTradedPokemons.forEach { pk ->
val idx = party.indexOf(pk)
if (idx != -1) newSelectedIndices.add(idx)
}
trade.oppositeAcceptedMyOffer.set(false)
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(Pair(theirTradedPokemon.pokemonId, PartyPosition(tradedSlot))))
selectedPartyIndices.clear()
selectedPartyIndices.addAll(newSelectedIndices)
val newOffers = selectedPartyIndices.mapNotNull { idx ->
party[idx]?.let { it.pokemonId to PartyPosition(idx) }
}
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(newOffers))
rebuildWidgets()
}
trade.oppositeOffer.subscribe { newOffer: Pokemon? ->
setOfferedPokemon(pokemon = newOffer, isOpposing = true)
trade.oppositeOffer.subscribe { newOffer: List<Pokemon> ->
setOfferedPokemon(pokemonList = newOffer, isOpposing = true)
}
trade.myOffer.subscribe { myOffer: Pokemon? ->
setOfferedPokemon(pokemon = myOffer)
trade.myOffer.subscribe { myOffer: List<Pokemon> ->
setOfferedPokemon(pokemonList = myOffer)
}
trade.oppositeAcceptedMyOffer.subscribe {
@@ -216,9 +262,16 @@ class TradeGUI(
pokemon = pokemon,
parent = this,
onPress = {
if (!trade.acceptedOppositeOffer && !tradeProcessing) {
val pk = if (offeredPokemon?.uuid == pokemon?.pokemonId) null else pokemon
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(pk?.let { it.pokemonId to PartyPosition(partyIndex) }))
if (!trade.acceptedOppositeOffer && !tradeProcessing && pokemon != null) {
if (selectedPartyIndices.contains(partyIndex)) {
selectedPartyIndices.remove(partyIndex)
} else if (selectedPartyIndices.size < 6) {
selectedPartyIndices.add(partyIndex)
}
val newOffers = selectedPartyIndices.mapNotNull { idx ->
party[idx]?.let { it.pokemonId to PartyPosition(idx) }
}
CobblemonNetwork.sendToServer(UpdateTradeOfferPacket(newOffers))
playSound(CobblemonSounds.GUI_CLICK)
}
}
@@ -250,8 +303,8 @@ class TradeGUI(
).also { widget -> addRenderableWidget(widget) }
}
setOfferedPokemon(pokemon = offeredPokemon, isOpposing = false)
setOfferedPokemon(pokemon = opposingOfferedPokemon, isOpposing = true)
setOfferedPokemon(pokemonList = offeredPokemons.toList(), isOpposing = false)
setOfferedPokemon(pokemonList = opposingPokemons.toList(), isOpposing = true)
offeredPokemonMarkings = MarkingsWidget(x + 74.5, y + 21, offeredPokemon, false)
opposingOfferedPokemonMarkings = MarkingsWidget(x + 177.5, y + 21, opposingOfferedPokemon, false)
@@ -284,7 +337,7 @@ class TradeGUI(
if (tradeAnimationProgress > REVERSE_ANIMATION_TICK)
(REVERSE_ANIMATION_TICK + TRADE_ANIMATION_FRAMES - tradeAnimationProgress)
else (tradeAnimationProgress - MODEL_SCALE_BUFFER_FRAMES)
) * TRADE_ANIMATION_HEIGHT
) * TRADE_ANIMATION_HEIGHT
blitk(
matrixStack = matrices,
texture = tradeAnimationResource,
@@ -313,20 +366,56 @@ class TradeGUI(
// Render Model Portraits
var scale = PORTRAIT_SCALE
var scaleOffsetY = PORTRAIT_MODEL_OFFSET_Y
var isShrinking = false
if (tradeProcessing) {
val scaleInterval = if (tradeAnimationProgress > REVERSE_ANIMATION_TICK) (MAX_TRADE_PROGRESS - tradeAnimationProgress) else tradeAnimationProgress
scale = (PORTRAIT_SCALE - (scaleInterval * PORTRAIT_SCALE_MULTIPLIER)).coerceIn(0F, PORTRAIT_SCALE)
scaleOffsetY = (45.0 - (45.0 / PORTRAIT_SCALE) * scale) + PORTRAIT_MODEL_OFFSET_Y
isShrinking = true
}
val shrinkPercentage = if (isShrinking) 1.0F - (scale / PORTRAIT_SCALE) else 0.0F
val myCenterXDest = x + 70 + (PORTRAIT_SIZE / 2.0)
val myCenterYDest = y + 30 + (PORTRAIT_SIZE / 2.0)
val opCenterXDest = x + 145 + (PORTRAIT_SIZE / 2.0)
val opCenterYDest = y + 30 + (PORTRAIT_SIZE / 2.0)
if (scale > 0F) {
context.enableScissor(backgroundX, backgroundY, backgroundX + BASE_BACKGROUND_WIDTH, backgroundY + BASE_BACKGROUND_HEIGHT)
offeredPokemonModel?.baseScale = scale
offeredPokemonModel?.offsetY = scaleOffsetY
offeredPokemonModel?.render(context, mouseX, mouseY, delta)
opposingOfferedPokemonModel?.baseScale = scale
opposingOfferedPokemonModel?.offsetY = scaleOffsetY
opposingOfferedPokemonModel?.render(context, mouseX, mouseY, delta)
offeredPokemonModels.forEachIndexed { i, model ->
val posAndScale = getModelPositionAndScale(i, offeredPokemons.size, x + 70, y + 30)
val originalScale = posAndScale.second
model.baseScale = if (isShrinking) (originalScale / PORTRAIT_SCALE) * scale else originalScale
model.offsetY = if (isShrinking) scaleOffsetY else PORTRAIT_MODEL_OFFSET_Y
val startCenterX = posAndScale.first.first + (PORTRAIT_SIZE / 2.0)
val startCenterY = posAndScale.first.second + (PORTRAIT_SIZE / 2.0)
context.pose().pushPose()
context.pose().translate((myCenterXDest - startCenterX) * shrinkPercentage, (myCenterYDest - startCenterY) * shrinkPercentage, 0.0)
model.render(context, mouseX, mouseY, delta)
context.pose().popPose()
}
opposingOfferedPokemonModels.forEachIndexed { i, model ->
val posAndScale = getModelPositionAndScale(i, opposingPokemons.size, x + 145, y + 30)
val originalScale = posAndScale.second
model.baseScale = if (isShrinking) (originalScale / PORTRAIT_SCALE) * scale else originalScale
model.offsetY = if (isShrinking) scaleOffsetY else PORTRAIT_MODEL_OFFSET_Y
val startCenterX = posAndScale.first.first + (PORTRAIT_SIZE / 2.0)
val startCenterY = posAndScale.first.second + (PORTRAIT_SIZE / 2.0)
context.pose().pushPose()
context.pose().translate((opCenterXDest - startCenterX) * shrinkPercentage, (opCenterYDest - startCenterY) * shrinkPercentage, 0.0)
model.render(context, mouseX, mouseY, delta)
context.pose().popPose()
}
context.disableScissor()
}
@@ -341,8 +430,46 @@ class TradeGUI(
renderInfoLabels(context, x, y)
renderPokemonInfo(offeredPokemon, false, context, x, y, mouseX, mouseY)
renderPokemonInfo(opposingOfferedPokemon, true, context, x, y, mouseX, mouseY)
children().filterIsInstance<PartySlot>().forEach { slot ->
if (slot.isHovered(mouseX, mouseY)) {
val hoveredId = slot.pokemon?.pokemonId
if (slot.isOpposing && slot.hasSelected()) {
opposingPokemons.find { it.uuid == hoveredId }?.let { opposingOfferedPokemon = it }
} else if (!slot.isOpposing) {
val matchingModel = offeredPokemons.find { it.uuid == hoveredId }
if (matchingModel != null) {
offeredPokemon = matchingModel
} else {
val realPokemon = com.cobblemon.mod.common.client.CobblemonClient.storage.party.find { it?.uuid == hoveredId }
if (realPokemon != null) {
offeredPokemon = realPokemon
}
}
}
}
}
val currentOffered = offeredPokemon
if (currentOffered != null) {
renderPokemonInfo(currentOffered, false, context, x, y, mouseX, mouseY)
if (::offeredPokemonMarkings.isInitialized) {
offeredPokemonMarkings.visible = true
if (offeredPokemonMarkings.pokemon != currentOffered) offeredPokemonMarkings.setActivePokemon(currentOffered)
}
} else {
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.visible = false
}
val currentOpposing = opposingOfferedPokemon
if (currentOpposing != null) {
renderPokemonInfo(currentOpposing, true, context, x, y, mouseX, mouseY)
if (::opposingOfferedPokemonMarkings.isInitialized) {
opposingOfferedPokemonMarkings.visible = true
if (opposingOfferedPokemonMarkings.pokemon != currentOpposing) opposingOfferedPokemonMarkings.setActivePokemon(currentOpposing)
}
} else {
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.visible = false
}
if (trade.acceptedOppositeOffer) {
blitk(
@@ -431,19 +558,25 @@ class TradeGUI(
super.render(context, mouseX, mouseY, delta)
// Item Tooltip
if (offeredPokemon != null && !offeredPokemon!!.heldItemNoCopy().isEmpty) {
val itemX = x + 50
val itemY = y + 125
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, offeredPokemon!!.heldItemNoCopy(), mouseX, mouseY)
val ttOffered = offeredPokemon
if (ttOffered != null) {
// Item Tooltip
if (!ttOffered.heldItemNoCopy().isEmpty) {
val itemX = x + 50
val itemY = y + 125
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, ttOffered.heldItemNoCopy(), mouseX, mouseY)
}
}
if (opposingOfferedPokemon != null && !opposingOfferedPokemon!!.heldItemNoCopy().isEmpty) {
val itemX = x + 227
val itemY = y + 125
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, opposingOfferedPokemon!!.heldItemNoCopy(), mouseX, mouseY)
val ttOpposing = opposingOfferedPokemon
if (ttOpposing != null) {
if (!ttOpposing.heldItemNoCopy().isEmpty) {
val itemX = x + 227
val itemY = y + 125
val itemHovered = mouseX.toFloat() in (itemX.toFloat()..(itemX.toFloat() + 16)) && mouseY.toFloat() in (itemY.toFloat()..(itemY.toFloat() + 16))
if (itemHovered) context.renderTooltip(Minecraft.getInstance().font, ttOpposing.heldItemNoCopy(), mouseX, mouseY)
}
}
}
@@ -480,11 +613,13 @@ class TradeGUI(
}
if (tradeAnimationProgress == (MAX_TRADE_PROGRESS + CRY_BUFFER)) {
if (offeredPokemonModel !== null && opposingOfferedPokemonModel !== null) {
offeredPokemonModel!!.state.activeAnimations.clear()
offeredPokemonModel!!.state.addFirstAnimation(setOf("cry"))
opposingOfferedPokemonModel!!.state.activeAnimations.clear()
opposingOfferedPokemonModel!!.state.addFirstAnimation(setOf("cry"))
offeredPokemonModels.forEach {
it.state.activeAnimations.clear()
it.state.addFirstAnimation(setOf("cry"))
}
opposingOfferedPokemonModels.forEach {
it.state.activeAnimations.clear()
it.state.addFirstAnimation(setOf("cry"))
}
}
@@ -515,41 +650,140 @@ class TradeGUI(
override fun renderMenuBackground(context: GuiGraphics) {}
private fun setOfferedPokemon(pokemon: Pokemon?, isOpposing: Boolean = false) {
private fun setOfferedPokemon(pokemonList: List<Pokemon>, isOpposing: Boolean = false) {
protectiveTicks = 20
if (isOpposing) {
val wasEmpty = opposingPokemons.isEmpty()
val currentViewedUuid = if (opposingGridMode) null else opposingOfferedPokemon?.uuid
opposingPokemons.clear()
opposingPokemons.addAll(pokemonList)
val matchingPokemon = opposingPokemons.find { it.uuid == currentViewedUuid }
if (matchingPokemon != null) {
opposingOfferedPokemon = matchingPokemon
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.setActivePokemon(opposingOfferedPokemon)
} else {
opposingOfferedPokemon = opposingPokemons.lastOrNull()
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.setActivePokemon(opposingOfferedPokemon)
if (wasEmpty && opposingOfferedPokemon != null) {
opposingGridMode = false
} else if (!opposingGridMode) {
opposingGridMode = true
}
}
trade.acceptedOppositeOffer = false
} else {
val wasEmpty = offeredPokemons.isEmpty()
val currentViewedUuid = if (myGridMode) null else offeredPokemon?.uuid
offeredPokemons.clear()
offeredPokemons.addAll(pokemonList)
val matchingPokemon = offeredPokemons.find { it.uuid == currentViewedUuid }
if (matchingPokemon != null) {
offeredPokemon = matchingPokemon
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.setActivePokemon(offeredPokemon)
} else {
offeredPokemon = offeredPokemons.lastOrNull()
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.setActivePokemon(offeredPokemon)
if (wasEmpty && offeredPokemon != null) {
myGridMode = false
} else if (!myGridMode) {
myGridMode = true
}
}
}
rebuildSelectedModels(isOpposing)
}
private fun rebuildSelectedModels(isOpposing: Boolean) {
val x = (width - BASE_WIDTH) / 2
val y = (height - BASE_HEIGHT) / 2
if (isOpposing) {
opposingOfferedPokemon = pokemon
opposingOfferedPokemonModel = if (pokemon != null) ModelWidget(
pX = x + 145,
pY = y + 30,
pWidth = PORTRAIT_SIZE,
pHeight = PORTRAIT_SIZE,
pokemon = pokemon.asRenderablePokemon(),
baseScale = PORTRAIT_SCALE,
rotationY = 35F,
offsetY = PORTRAIT_MODEL_OFFSET_Y
) else null
if (::opposingOfferedPokemonMarkings.isInitialized) opposingOfferedPokemonMarkings.setActivePokemon(opposingOfferedPokemon)
trade.acceptedOppositeOffer = false
opposingOfferedPokemonModels.clear()
val total = opposingPokemons.size
opposingPokemons.forEachIndexed { i, pk ->
val (pos, currentScale) = getModelPositionAndScale(i, total, x + 145, y + 30)
opposingOfferedPokemonModels.add(ModelWidget(
pX = pos.first, pY = pos.second,
pWidth = PORTRAIT_SIZE, pHeight = PORTRAIT_SIZE,
pokemon = pk.asRenderablePokemon(),
baseScale = currentScale,
rotationY = 35F,
offsetY = PORTRAIT_MODEL_OFFSET_Y
))
}
} else {
offeredPokemon = pokemon
offeredPokemonModel = if (pokemon != null) ModelWidget(
pX = x + 70,
pY = y + 30,
pWidth = PORTRAIT_SIZE,
pHeight = PORTRAIT_SIZE,
pokemon = pokemon.asRenderablePokemon(),
baseScale = PORTRAIT_SCALE,
rotationY = 325F,
offsetY = PORTRAIT_MODEL_OFFSET_Y
) else null
if (::offeredPokemonMarkings.isInitialized) offeredPokemonMarkings.setActivePokemon(offeredPokemon)
offeredPokemonModels.clear()
val total = offeredPokemons.size
offeredPokemons.forEachIndexed { i, pk ->
val (pos, currentScale) = getModelPositionAndScale(i, total, x + 70, y + 30)
offeredPokemonModels.add(ModelWidget(
pX = pos.first, pY = pos.second,
pWidth = PORTRAIT_SIZE, pHeight = PORTRAIT_SIZE,
pokemon = pk.asRenderablePokemon(),
baseScale = currentScale,
rotationY = 325F,
offsetY = PORTRAIT_MODEL_OFFSET_Y
))
}
}
}
private fun getModelPositionAndScale(index: Int, total: Int, baseX: Int, baseY: Int): Pair<Pair<Int, Int>, Float> {
val scale = when (total) {
1 -> PORTRAIT_SCALE
2 -> PORTRAIT_SCALE * 0.75f
3 -> PORTRAIT_SCALE * 0.65f
4 -> PORTRAIT_SCALE * 0.55f
else -> PORTRAIT_SCALE * 0.5f
}
if (total <= 1) {
return Pair(Pair(baseX, baseY), scale)
}
val rowLength1 = when (total) {
2, 3, 4 -> 2
else -> 3
}
val rowLength2 = total - rowLength1
val isBottomRow = index >= rowLength1
val row = if (isBottomRow) 1 else 0
val col = if (isBottomRow) index - rowLength1 else index
val currentRowLength = if (isBottomRow) rowLength2 else rowLength1
val colSpacing = when (total) {
2 -> 35
3 -> 30
4 -> 25
else -> 23
}
val rowSpacing = when (total) {
2, 3 -> 30
4 -> 28
else -> 25
}
val totalWidth = (currentRowLength - 1) * colSpacing
val startX = baseX - (totalWidth / 2)
val totalHeight = if (rowLength2 > 0) rowSpacing else 0
var startY = baseY + 12 - (totalHeight / 2)
if (total >= 4) {
startY += 8
}
val posX = startX + (col * colSpacing)
val posY = startY + (row * rowSpacing)
return Pair(Pair(posX, posY), scale)
}
private fun playSound(soundEvent: SoundEvent): SoundInstance {
val soundInstance = SimpleSoundInstance.forUI(soundEvent, 1.0F)
Minecraft.getInstance().soundManager.play(soundInstance)
@@ -557,10 +791,10 @@ class TradeGUI(
}
private fun cancelTradeSound() {
if (tradeSoundInstance !== null) Minecraft.getInstance().soundManager.stop(tradeSoundInstance)
if (tradeSoundInstance != null) Minecraft.getInstance().soundManager.stop(tradeSoundInstance!!)
}
private fun renderPokemonInfo(pokemon: Pokemon?, isOpposing: Boolean, context: GuiGraphics, x: Int, y: Int, mouseX: Int, mouseY: Int) {
public fun renderPokemonInfo(pokemon: Pokemon?, isOpposing: Boolean, context: GuiGraphics, x: Int, y: Int, mouseX: Int, mouseY: Int) {
if (pokemon != null) {
val matrices = context.pose()
// Level
@@ -1047,4 +1281,4 @@ class TradeGUI(
scale = SCALE
)
}
}
}
@@ -0,0 +1,30 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.net.bag
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
import com.cobblemon.mod.common.net.messages.client.bag.UpdateBagMenuDataPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object UpdateBagMenuDataHandler : ServerNetworkPacketHandler<UpdateBagMenuDataPacket> {
override fun handle(packet: UpdateBagMenuDataPacket, server: MinecraftServer, player: ServerPlayer) {
if (player.containerMenu !is CobblemonBagMenu) {
Cobblemon.LOGGER.debug("Player {} interacted with invalid menu {}", player, player.containerMenu)
return
}
val menu = player.containerMenu as? CobblemonBagMenu ?: return
menu.currentlySelectedCategory = packet.categoryId
menu.scroll = packet.scroll
menu.setupSlotsBasedOnContent()
}
}
@@ -0,0 +1,28 @@
/*
* 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.net.bag
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.item.bag.CobblemonBagMenu
import com.cobblemon.mod.common.net.messages.client.bag.UseItemOnPokemonFromBagPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object UseItemOnPokemonFromBagHandler: ServerNetworkPacketHandler<UseItemOnPokemonFromBagPacket> {
override fun handle(packet: UseItemOnPokemonFromBagPacket, server: MinecraftServer, player: ServerPlayer) {
if (player.containerMenu !is CobblemonBagMenu) {
Cobblemon.LOGGER.debug("Player {} interacted with invalid menu {}", player, player.containerMenu)
return
}
val menu = player.containerMenu as? CobblemonBagMenu ?: return
menu.useHeldItemOnPokemon(packet.partySlot)
}
}
@@ -15,9 +15,9 @@ import net.minecraft.client.Minecraft
object TradeAcceptanceChangedHandler : ClientNetworkPacketHandler<TradeAcceptanceChangedPacket> {
override fun handle(packet: TradeAcceptanceChangedPacket, client: Minecraft) {
if (CobblemonClient.trade?.myOffer?.get()?.uuid == packet.pokemonId) {
if (CobblemonClient.trade?.myOffer?.get()?.any { it.uuid == packet.pokemonId } == true) {
CobblemonClient.trade!!.oppositeAcceptedMyOffer.set(packet.accepted)
} else if (CobblemonClient.trade?.oppositeOffer?.get()?.uuid == packet.pokemonId) {
} else if (CobblemonClient.trade?.oppositeOffer?.get()?.any { it.uuid == packet.pokemonId } == true) {
CobblemonClient.trade!!.acceptedOppositeOffer = packet.accepted
}
}
@@ -16,6 +16,6 @@ import net.minecraft.client.Minecraft
object TradeCompletedHandler : ClientNetworkPacketHandler<TradeCompletedPacket> {
override fun handle(packet: TradeCompletedPacket, client: Minecraft) {
val trade = CobblemonClient.trade ?: return
trade.completedEmitter.emit(packet.pokemonId1 to packet.pokemonId2)
trade.completedEmitter.emit(packet.pokemonIds1 to packet.pokemonIds2)
}
}
@@ -20,7 +20,7 @@ import net.minecraft.client.Minecraft
object TradeStartedHandler : ClientNetworkPacketHandler<TradeStartedPacket> {
override fun handle(packet: TradeStartedPacket, client: Minecraft) {
Cobblemon.LOGGER.error("handle trade start from: " + packet.traderId)
Cobblemon.LOGGER.info("Trade started with {}", packet.traderId)
val trade = ClientTrade()
CobblemonClient.trade = trade
Minecraft.getInstance().setScreen(
@@ -18,9 +18,9 @@ object TradeUpdatedHandler : ClientNetworkPacketHandler<TradeUpdatedPacket> {
val trade = CobblemonClient.trade ?: return
if (packet.playerId == Minecraft.getInstance().player?.uuid) {
trade.myOffer.set(packet.pokemon)
trade.myOffer.set(packet.pokemons)
} else {
trade.oppositeOffer.set(packet.pokemon)
trade.oppositeOffer.set(packet.pokemons)
}
trade.oppositeAcceptedMyOffer.set(false)
trade.acceptedOppositeOffer = false
@@ -0,0 +1,65 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.layer
import com.cobblemon.mod.common.client.CobblemonBakingOverrides
import com.cobblemon.mod.common.item.bag.CobblemonBagItem
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.client.Minecraft
import net.minecraft.client.model.PlayerModel
import net.minecraft.client.renderer.MultiBufferSource
import net.minecraft.client.renderer.entity.RenderLayerParent
import net.minecraft.client.renderer.entity.layers.RenderLayer
import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemDisplayContext
class CobblemonBagRenderer<T : Player>(renderLayerParent: RenderLayerParent<T, PlayerModel<T>>): RenderLayer<T, PlayerModel<T>>(renderLayerParent) {
override fun render(
poseStack: PoseStack,
bufferSource: MultiBufferSource,
packedLight: Int,
player: T,
limbSwing: Float,
limbSwingAmount: Float,
partialTick: Float,
ageInTicks: Float,
netHeadYaw: Float,
headPitch: Float
) {
val bagItem = player.getItemBySlot(EquipmentSlot.CHEST)
if (bagItem.item !is CobblemonBagItem) return
val model = CobblemonBakingOverrides.bagModels[bagItem.item]?.getModel() ?: return
player.level().profiler.push("cobblemon_bag_render")
poseStack.pushPose()
parentModel.body.translateAndRotate(poseStack)
poseStack.rotateAround(Axis.ZP.rotationDegrees(180F), 0F, 1F, 0F)
poseStack.translate(0.0, 1.0, 0.0)
Minecraft.getInstance().itemRenderer.render(
bagItem,
ItemDisplayContext.FIXED,
false,
poseStack,
bufferSource,
packedLight,
OverlayTexture.NO_OVERLAY,
model
)
poseStack.popPose()
player.level().profiler.pop()
}
}
@@ -14,12 +14,12 @@ import com.cobblemon.mod.common.pokemon.Pokemon
import java.util.UUID
class ClientTrade {
var myOffer = SettableObservable<Pokemon?>(null)
var oppositeOffer = SettableObservable<Pokemon?>(null)
var myOffer = SettableObservable<List<Pokemon>>(emptyList())
var oppositeOffer = SettableObservable<List<Pokemon>>(emptyList())
var tradeProcessing = SettableObservable(Pair(false, false))
var oppositeAcceptedMyOffer = SettableObservable(false)
var acceptedOppositeOffer = false
var completedEmitter = SimpleObservable<Pair<UUID, UUID>>()
var completedEmitter = SimpleObservable<Pair<List<UUID>, List<UUID>>>()
var cancelEmitter = SimpleObservable<Unit>()
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.data
import com.cobblemon.mod.common.*
import com.cobblemon.mod.common.Cobblemon.LOGGER
import com.cobblemon.mod.common.api.abilities.Abilities
import com.cobblemon.mod.common.api.bag.BagCategories
import com.cobblemon.mod.common.api.berry.Berries
import com.cobblemon.mod.common.api.cooking.Seasonings
import com.cobblemon.mod.common.api.data.DataProvider
@@ -120,6 +121,7 @@ object CobblemonDataProvider : DataProvider {
this.register(SpawnBaitEffects, reloadable = false)
this.register(CobblemonRideSettings, reloadable = true)
this.register(TechnicalMachines, reloadable = false)
this.register(BagCategories, reloadable = false)
SpawnBait.Effects.setupEffects()
PlatformEvents.SERVER_PLAYER_LOGOUT.subscribe {
@@ -9,10 +9,7 @@
package com.cobblemon.mod.common.entity.ai
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
@@ -32,23 +29,12 @@ object FleeFromAttackerTask {
Trigger { _, entity, _ ->
val hurtByEntity = it.get(hurtByEntity)
val avoidTarget = it.tryGet(avoidTarget).orElse(null)
if (avoidTarget != null && avoidTarget == hurtByEntity) {
if (avoidTarget != null && avoidTarget == hurtByEntity.uuid) {
return@Trigger false
}
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val avoidDurationTicks = mainThreadRuntime.resolveInt(avoidDurationTicks)
val pokemonEntity = entity as? PokemonEntity
pokemonEntity?.let {
it.after(1.0f) {
it.playMoodParticle("mood_stress")
}
}
entity.brain.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, hurtByEntity, avoidDurationTicks.toLong())
return@Trigger true
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.entity.ai
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
@@ -25,16 +24,10 @@ object GetAngryAtAttackerTask {
Trigger { _, entity, _ ->
val hurtByEntity = it.get(hurtByEntity)
val angryAt = it.tryGet(angryAt).orElse(null)
if (angryAt != null && angryAt == hurtByEntity.uuid) {
return@Trigger false
}
val becomingAngry = angryAt == null
entity.brain.setMemory(MemoryModuleType.ANGRY_AT, hurtByEntity.uuid)
if (becomingAngry) {
(entity as PokemonEntity?)?.playMoodParticle("mood_anger_steam")
}
return@Trigger true
}
}
@@ -160,5 +160,4 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
private const val INVENTORY_SLOTS = 27
}
}
@@ -34,9 +34,6 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
import com.cobblemon.mod.common.api.net.serializers.PlatformTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.PoseTypeDataSerializer
import com.cobblemon.mod.common.api.net.serializers.RideBoostsDataSerializer
@@ -428,9 +425,6 @@ open class PokemonEntity(
var occupiedSeats = mutableMapOf<Seat, Entity>()
var moodParticleLock = false
var playedZeroStaminaParticle = false
init {
delegate.initialize(this)
delegate.changePokemon(pokemon)
@@ -910,27 +904,6 @@ open class PokemonEntity(
return future
}
fun playMoodParticle(particleName: String) {
if (moodParticleLock) return
val actionEffect = ActionEffects.actionEffects[cobblemonResource(particleName)] ?: return
cry()
val context = ActionEffectContext(
actionEffect = actionEffect,
providers = mutableListOf(UsersProvider(this)),
runtime = runtime,
level = level()
)
actionEffect.run(context)
moodParticleLock = true
after(5.0f) {
moodParticleLock = false
}
}
override fun saveWithoutId(nbt: CompoundTag): CompoundTag {
val tethering = this.tethering
if (tethering != null) {
@@ -2342,8 +2315,6 @@ open class PokemonEntity(
ifRidingAvailable { _, _, state ->
state.stamina.set(entityData.get(RIDE_STAMINA))
}
playedZeroStaminaParticle = false
}
}
@@ -2372,26 +2343,12 @@ open class PokemonEntity(
override fun tickRidden(driver: Player, movementInput: Vec3) {
super.tickRidden(driver, movementInput)
ifRidingAvailable { behaviour, settings, state ->
behaviour.tick(settings, state, this, driver, movementInput)
if (entityData.get(RIDE_STAMINA) == -1F) {
state.stamina.set(1F, forced = true)
}
if (!level().isClientSide) {
val stamina = state.stamina.get()
if (stamina <= 0f) {
if (!playedZeroStaminaParticle) {
playMoodParticle("mood_stress")
playedZeroStaminaParticle = true
}
} else {
playedZeroStaminaParticle = false
}
}
if (!this.level().isClientSide) {
val pose = behaviour.pose(settings, state, this)
if (pose != this.entityData.get(POSE_TYPE)) {
@@ -2787,4 +2744,9 @@ open class PokemonEntity(
}
fun hasRider() = this.passengers.any { it is LivingEntity }
}
override fun playStepSound(pos: BlockPos, state: BlockState) {
if (this.pokemon.species.silenceStepSounds) return
super.playStepSound(pos, state)
}
}
@@ -44,11 +44,6 @@ class DefendOwnerSensor : Sensor<PokemonEntity>(10) {
return@findClosest false
}.map { mob -> mob as LivingEntity }
val previousAttacker = entity.brain.getMemorySafely(CobblemonMemories.NEAREST_VISIBLE_ATTACKER)
val isNewAttacker = nearestAttacker.isPresent && previousAttacker.isEmpty
entity.brain.setMemory(CobblemonMemories.NEAREST_VISIBLE_ATTACKER, nearestAttacker)
if (isNewAttacker) {
entity.playMoodParticle("mood_anger_steam")
}
}
}
@@ -9,7 +9,6 @@
package com.cobblemon.mod.common.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.getMemorySafely
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.ai.behavior.OneShot
@@ -61,12 +60,7 @@ object AttackHostileMobsTask {
}
if (hostile != null) {
val isNewTarget = currentTarget == null
entity.brain.setMemory(MemoryModuleType.ATTACK_TARGET, hostile)
if (isNewTarget) {
(entity as? PokemonEntity)?.playMoodParticle("mood_anger_steam")
}
true
} else {
false
@@ -91,7 +91,7 @@ class EatHeldItemTask(entity: PokemonEntity) : Behavior<PokemonEntity>(
}
this.timelastEaten = time
if (itemConfig != null && itemConfig.fullnessValue > 0) {
entity.pokemon.feedPokemon(itemConfig.fullnessValue, false, false)
entity.pokemon.feedPokemon(itemConfig.fullnessValue, false)
}
// Check if the result item is something that should be dropped
resultItemStack = entity.pokemon.heldItem()
@@ -31,6 +31,7 @@ import net.minecraft.world.item.BlockItem
import com.cobblemon.mod.common.platform.events.ServerPlayerEvent
import com.cobblemon.mod.common.api.storage.player.PlayerInstancedDataStoreTypes
import com.cobblemon.mod.common.item.GemItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.platform.events.PlatformEvents
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.getPlayer
@@ -164,26 +165,21 @@ object AdvancementHandler : EventHandler {
}
fun onTradeCompleted(event : TradeEvent.Post) {
val player1 = event.tradeParticipant1Pokemon.getOwnerPlayer()
val player2 = event.tradeParticipant2Pokemon.getOwnerPlayer()
if (player1 != null) {
CobblemonCriteria.TRADE_POKEMON.trigger(player1, TradePokemonContext(event.tradeParticipant1Pokemon, event.tradeParticipant2Pokemon))
val playerData = Cobblemon.playerDataManager.getGenericData(player1)
val advancementData = playerData.advancementData
advancementData.updateTotalTradedCount()
advancementData.updateAspectsCollected(player1, event.tradeParticipant2Pokemon)
CobblemonCriteria.COLLECT_ASPECT.trigger(player1, advancementData.aspectsCollected)
Cobblemon.playerDataManager.saveSingle(playerData, PlayerInstancedDataStoreTypes.GENERAL)
}
if (player2 != null) {
CobblemonCriteria.TRADE_POKEMON.trigger(player2, TradePokemonContext(event.tradeParticipant2Pokemon, event.tradeParticipant1Pokemon))
val playerData = Cobblemon.playerDataManager.getGenericData(player2)
val advancementData = playerData.advancementData
advancementData.updateTotalTradedCount()
advancementData.updateAspectsCollected(player2, event.tradeParticipant1Pokemon)
CobblemonCriteria.COLLECT_ASPECT.trigger(player2, advancementData.aspectsCollected)
Cobblemon.playerDataManager.saveSingle(playerData, PlayerInstancedDataStoreTypes.GENERAL)
onTradeCompleted(event.tradeParticipant1Pokemon, event.tradeParticipant2Pokemon)
onTradeCompleted(event.tradeParticipant2Pokemon, event.tradeParticipant1Pokemon)
}
private fun onTradeCompleted(received: List<Pokemon>, traded: List<Pokemon>) {
val player = received.firstOrNull()?.getOwnerPlayer() ?: return
received.forEach { receivedPokemon ->
traded.forEach { tradedPokemon -> CobblemonCriteria.TRADE_POKEMON.trigger(player, TradePokemonContext(tradedPokemon, receivedPokemon)) }
}
val playerData = Cobblemon.playerDataManager.getGenericData(player)
val advancementData = playerData.advancementData
advancementData.updateTotalTradedCount()
received.forEach { advancementData.updateAspectsCollected(player, it) }
CobblemonCriteria.COLLECT_ASPECT.trigger(player, advancementData.aspectsCollected)
Cobblemon.playerDataManager.saveSingle(playerData, PlayerInstancedDataStoreTypes.GENERAL)
}
/**
@@ -110,8 +110,8 @@ object StatHandler : EventHandler {
}
fun onTradeCompleted(event : TradeEvent.Post) {
event.tradeParticipant1Pokemon.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
event.tradeParticipant2Pokemon.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
event.tradeParticipant1Pokemon.firstOrNull()?.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
event.tradeParticipant2Pokemon.firstOrNull()?.getOwnerPlayer()?.awardStat(getStat(Cobblemon.statistics.TRADED))
}
fun onFossilRevived(event : FossilRevivedEvent) {
@@ -13,9 +13,7 @@ import com.cobblemon.mod.common.api.apricorn.Apricorn
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.client.pot.CookingQuality
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.chat.Component
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
@@ -94,9 +92,7 @@ class AprijuiceItem(val type: Apricorn): CobblemonItem(Properties().stacksTo(16)
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
val boosts = getBoosts(stack)
pokemon.addRideBoosts(boosts.mapValues { it.value.toFloat() })
@@ -96,9 +96,8 @@ class PokePuffItem : Item(Properties().stacksTo(64)), PokemonSelectingItem {
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(4)) {
return InteractionResultHolder.fail(stack)
}
// Feed the Pokémon 4 fullness points
pokemon.feedPokemon(4)
val friendshipChange = PokePuffUtils.calculateFriendshipChange(stack, pokemon.nature)
@@ -0,0 +1,99 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.bag
import net.minecraft.core.HolderLookup
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.world.Container
import net.minecraft.world.ContainerHelper
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import kotlin.math.min
class CobblemonBagContainer: Container {
val items: MutableList<ItemStack> = mutableListOf()
override fun getContainerSize(): Int {
return items.size
}
override fun getItem(slot: Int): ItemStack {
if (slot >= items.size) return ItemStack.EMPTY
return items[slot]
}
override fun isEmpty(): Boolean {
return items.isEmpty()
}
override fun removeItem(slot: Int, amount: Int): ItemStack {
if (slot >= items.size) return ItemStack.EMPTY
val itemStack = items[slot].split(amount)
if (items[slot].isEmpty) {
items.removeAt(slot)
}
return itemStack
}
override fun removeItemNoUpdate(slot: Int): ItemStack { return ItemStack.EMPTY }
override fun clearContent() {
items.clear()
setChanged()
}
override fun setChanged() {
}
override fun setItem(slot: Int, stack: ItemStack) {} // Obsolete
override fun stillValid(player: Player): Boolean {
return true
}
override fun getMaxStackSize(): Int {
return 99999
}
fun fromTag(tag: ListTag, levelRegistry: HolderLookup.Provider) {
items.clear()
tag.forEach {
val compoundTag = it as CompoundTag
val count = compoundTag.getInt("count")
compoundTag.putInt("count", 1)
val item = ItemStack.parse(levelRegistry, it)
item.ifPresent { theItem ->
theItem.count = count
items.add(theItem)
}
}
}
fun createTag(levelRegistry: HolderLookup.Provider): ListTag {
val tag = ListTag()
items.forEach {
val fakeItem = it.copyWithCount(1)
val compoundTag = fakeItem.save(levelRegistry) as CompoundTag
compoundTag.putInt("count", it.count) // ItemStack does not save count above 99 for some reason, so we save it ourselves
tag.add(compoundTag)
}
return tag
}
fun dropAll(player: Player) {
items.forEach {
player.drop(it, true, false)
}
items.clear()
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.bag
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.item.CobblemonItem
import com.cobblemon.mod.common.bag.BagEntity
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.core.Holder
import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.MenuProvider
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.item.Equipable
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
class CobblemonBagItem: CobblemonItem(Properties().stacksTo(1)), MenuProvider, Equipable {
override fun use(level: Level, player: Player, usedHand: InteractionHand): InteractionResultHolder<ItemStack?> {
val itemStack = player.getItemInHand(usedHand)
player.openMenu(this)
return InteractionResultHolder.success(itemStack)
}
override fun createMenu(i: Int, inventory: Inventory, player: Player): AbstractContainerMenu {
val bagEntity = (player as? BagEntity)
val container = bagEntity?.`cobblemon$getBagContainer`() ?: CobblemonBagContainer()
return CobblemonBagMenu(i, inventory, container)
}
override fun getDisplayName(): Component? {
return Component.translatable("cobblemon.container.bag")
}
override fun getEquipmentSlot(): EquipmentSlot = EquipmentSlot.CHEST
override fun getEquipSound(): Holder<SoundEvent?>? {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.ITEM_BAG_EQUIP, 1.0F))
return null
}
}
@@ -0,0 +1,428 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.bag
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonMenuType
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.advancement.criterion.PokemonInteractContext
import com.cobblemon.mod.common.api.bag.BagCategories
import com.cobblemon.mod.common.api.item.PokemonSelectingItem
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.util.isInBattle
import net.minecraft.CrashReport
import net.minecraft.ReportedException
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.core.NonNullList
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.InteractionResult
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Inventory
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.AbstractContainerMenu
import net.minecraft.world.inventory.ClickAction
import net.minecraft.world.inventory.ClickType
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import kotlin.math.ceil
import kotlin.math.max
import kotlin.math.min
class CobblemonBagMenu: AbstractContainerMenu {
companion object {
val BAG_SLOTS = 0..31
private val PARTY_SLOTS = 32..37
private val INVENTORY_SLOTS = 38..64
private val HOTBAR_SLOTS = 65..73
}
private val playerInventory: Inventory
private val bagContainer: CobblemonBagContainer
private val partyContainer: SimpleContainer
private var serverParty: PartyStore? = null
var currentlySelectedCategory = -1
var scroll = 0
var maxScroll = 0
var categoryItemCounts = mutableListOf<Int>()
constructor(containerId: Int, playerInventory: Inventory): super(CobblemonMenuType.COBBLEMON_BAG, containerId) {
this.playerInventory = playerInventory
this.bagContainer = CobblemonBagContainer()
this.partyContainer = SimpleContainer(6)
for (i in 0..<BagCategories.getOrderedCategoriesList().size) { //Pre-init so Screen does not crash
categoryItemCounts.add(0)
}
initializeSlots(playerInventory)
}
constructor(containerId: Int, playerInventory: Inventory, bagContainer: CobblemonBagContainer): super(CobblemonMenuType.COBBLEMON_BAG, containerId) {
this.playerInventory = playerInventory
this.bagContainer = bagContainer
this.partyContainer = SimpleContainer(6)
this.serverParty = Cobblemon.storage.getParty(playerInventory.player as ServerPlayer)
initializeSlots(playerInventory)
setupSlotsBasedOnContent()
calculateCategoriesItemCounts()
}
private fun initializeSlots(playerInventory: Inventory) {
for ((index, _) in BAG_SLOTS.withIndex()) {
val i = index / 8
val j = index % 8
addSlot(CobblemonBagSlot(bagContainer, -1, 81 + j * 18, 50 + i * 18))
}
for ((index, _) in PARTY_SLOTS.withIndex()) {
val xPos = 46
val yPos = 32 + 33 * index
if (serverParty != null) {
addSlot(CobblemonBagPartySlot(partyContainer, index,
{ serverParty?.get(index) != null },
{ serverParty?.get(index)?.heldItem() ?: ItemStack.EMPTY },
xPos, yPos))
} else {
addSlot(CobblemonBagPartySlot(partyContainer, index,
{ CobblemonClient.storage.party.get(index) != null },
{ CobblemonClient.storage.party.get(index)?.heldItem() ?: ItemStack.EMPTY },
xPos, yPos))
}
}
for ((index, _) in INVENTORY_SLOTS.withIndex()) {
val i = index / 9
val j = index % 9
addSlot(Slot(playerInventory, 9 + index, 81 + j * 18, 142 + i * 18))
}
for ((index, _) in HOTBAR_SLOTS.withIndex()) {
addSlot(Slot(playerInventory, index, 81 + index * 18, 200))
}
}
fun setupSlotsBasedOnContent() {
BAG_SLOTS.forEach {
(getSlot(it) as CobblemonBagSlot).let {
it.simulatedSlot = -1
it.slotOverflowValue = 0
}
}
var slotId = BAG_SLOTS.first - 8 * scroll
bagContainer.items.forEachIndexed { index, itemStack ->
if (currentlySelectedCategory == -1 || BagCategories.getOrderedCategoriesList()[currentlySelectedCategory].itemIsValid(itemStack)) {
val stackCount = ceil(itemStack.count.toFloat() / itemStack.maxStackSize).toInt()
for (i in 0..<stackCount) {
if (slotId in BAG_SLOTS) {
val slot = getSlot(slotId) as CobblemonBagSlot
slot.simulatedSlot = index
slot.slotOverflowValue = i
}
slotId += 1
if (slotId > BAG_SLOTS.last) return@forEachIndexed
}
}
}
updateMaxScroll()
}
fun calculateCategoriesItemCounts() {
val orderedCategories = BagCategories.getOrderedCategoriesList()
categoryItemCounts.clear()
for (i in 0..<orderedCategories.size) {
categoryItemCounts.add(0)
}
bagContainer.items.forEach { itemStack ->
for (i in 0..<orderedCategories.size) {
val category = orderedCategories[i]
if (category.itemIsValid(itemStack)) {
categoryItemCounts[i] += itemStack.count
return@forEach
}
}
}
}
fun changeCategory(categoryId: Int) {
currentlySelectedCategory = categoryId
scroll = 0
setupSlotsBasedOnContent()
}
fun updateMaxScroll() {
var itemCount = 0
bagContainer.items.forEach { itemStack ->
if (currentlySelectedCategory == -1 || BagCategories.getOrderedCategoriesList()[currentlySelectedCategory].itemIsValid(itemStack)) {
itemCount += ceil(itemStack.count.toFloat() / itemStack.maxStackSize).toInt()
}
}
val rowsWithItems = (itemCount - 1) / 8 + 1
maxScroll = max(rowsWithItems - 3, 0)
}
override fun stillValid(player: Player): Boolean {
return true //Add some extra logic like checking for maybe bag item existance
}
override fun sendAllDataToRemote() { //We want to send more info than just the slots
val items = NonNullList.create<ItemStack>()
for (i in PARTY_SLOTS.first..HOTBAR_SLOTS.last) {
this.remoteSlots[i] = this.slots[i].item.copy()
items.add(this.remoteSlots[i].copy())
}
this.remoteCarried = this.carried.copy()
bagContainer.items.forEach {
items.add(it.copy())
}
if (this.synchronizer != null) {
this.synchronizer!!.sendInitialData(this, items, this.remoteCarried, this.remoteDataSlots.toIntArray())
}
}
override fun initializeContents(stateId: Int, items: List<ItemStack>, carried: ItemStack) {
bagContainer.items.clear()
items.forEachIndexed { index, stack ->
val adjustedIndex = index + PARTY_SLOTS.first
if (adjustedIndex in PARTY_SLOTS.first..HOTBAR_SLOTS.last) {
this.slots[adjustedIndex].set(stack)
} else {
bagContainer.items.add(stack)
}
}
this.carried = carried
this.stateId = stateId
setupSlotsBasedOnContent()
calculateCategoriesItemCounts()
}
override fun clicked(slotId: Int, button: Int, clickType: ClickType, player: Player) {
if (slotId in BAG_SLOTS) {
try {
doBagClick(slotId, button, clickType, player)
} catch (exception: Exception) {
val crashReport = CrashReport.forThrowable(exception, "Container click")
val crashReportCategory = crashReport.addCategory("Click info")
crashReportCategory.setDetail("Menu Type") {
if (this.menuType != null) BuiltInRegistries.MENU.getKey(this.menuType!!)
.toString() else "<no type>"
}
crashReportCategory.setDetail("Menu Class") { this.javaClass.getCanonicalName() }
crashReportCategory.setDetail("Slot Count", this.slots.size)
crashReportCategory.setDetail("Slot", slotId)
crashReportCategory.setDetail("Button", button)
crashReportCategory.setDetail("Type", clickType)
throw ReportedException(crashReport)
}
return
} else if (slotId in PARTY_SLOTS) {
onPartySlotClick(slotId, button, clickType, player)
}
super.clicked(slotId, button, clickType, player)
}
// We handle the bag slots completely differently than normal slots, but a lot of default logic is cut
private fun doBagClick(slotId: Int, button: Int, clickType: ClickType, player: Player) {
val slot = (getSlot(slotId) as? CobblemonBagSlot) ?: return
val slotItem = slot.item
if (clickType == ClickType.PICKUP && (button == 0 || button == 1)) {
val primaryButton = if (button == 0) ClickAction.PRIMARY else ClickAction.SECONDARY
val carriedStack = carried
if (carriedStack.isEmpty) {
if (!slotItem.isEmpty) {
val pickupCount = if (primaryButton == ClickAction.PRIMARY) slotItem.count else (slotItem.count + 1) / 2
val removedItemStack = slot.remove(pickupCount)
if (!removedItemStack.isEmpty) {
carried = removedItemStack
slot.onTake(player, removedItemStack)
getAndReduceCategoryCount(removedItemStack)
}
playSound(CobblemonSounds.ITEM_BAG_ITEM_OUT)
setupSlotsBasedOnContent()
}
} else {
val (categoryId, allowedTransfer) = calculateInsertCount(carriedStack)
if (allowedTransfer <= 0) return
val transferCount = if (primaryButton == ClickAction.PRIMARY) min(carriedStack.count, allowedTransfer) else 1
bagContainer.items.forEachIndexed { index, slotStack ->
if (ItemStack.isSameItemSameComponents(carriedStack, slotStack) && !tryItemClickBehaviourOverride(player, primaryButton, CobblemonBagSlot(bagContainer, index, 0, 0), slotStack, carriedStack)
) { // Provide a slot with index equal to the item, as there is not really any 'real' slots
slotStack.grow(transferCount)
carriedStack.shrink(transferCount)
categoryItemCounts[categoryId] += transferCount
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
setupSlotsBasedOnContent()
return
}
}
val transferItem = carriedStack.split(transferCount)
bagContainer.items.add(transferItem)
categoryItemCounts[categoryId] += transferCount
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
setupSlotsBasedOnContent()
}
} else if (clickType == ClickType.QUICK_MOVE && (button == 0 || button == 1)) {
quickMoveStackFromBag(player, slot)
} else if (clickType == ClickType.CLONE && player.hasInfiniteMaterials() && carried.isEmpty && !slotItem.isEmpty) {
carried = slotItem.copyWithCount(slotItem.maxStackSize)
} else if (clickType == ClickType.THROW && carried.isEmpty && !slotItem.isEmpty) {
val throwCount = if (button == 0) 1 else min(slotItem.count, slotItem.maxStackSize)
val bagSize = bagContainer.items.size
val removedItemStack = slot.remove(throwCount)
player.drop(removedItemStack, true)
playSound(CobblemonSounds.ITEM_BAG_ITEM_OUT)
getAndReduceCategoryCount(removedItemStack)
if (bagSize != bagContainer.items.size) {
setupSlotsBasedOnContent()
}
}
}
//Handle party slot clicked
private fun onPartySlotClick(slotId: Int, button: Int, clickType: ClickType, player: Player) {
val slotItem = getSlot(slotId).item
if (clickType == ClickType.CLONE && player.hasInfiniteMaterials() && carried.isEmpty && !slotItem.isEmpty) {
carried = slotItem.copyWithCount(slotItem.maxStackSize)
} else if (clickType != ClickType.PICKUP) return
if (serverParty == null) return // Client does nothing
if (!carried.isEmpty && (isBlacklisted(carried) || !isWhitelisted(carried))) return //Blacklisted and Whitelisted checks
val index = slotId - PARTY_SLOTS.first
val pokemon = serverParty?.get(index) ?: return
val heldItem = pokemon.heldItem()
if (carried.isEmpty || carried.count <= 1) { // Can Swap/Pickup
val returned = pokemon.swapHeldItem(carried, aiCanDrop = false)
carried = returned
broadcastChanges()
} else if (heldItem.isEmpty) { // Can Placedown
pokemon.swapHeldItem(carried, aiCanDrop = false)
broadcastChanges()
}
}
//Handled only by server
fun useHeldItemOnPokemon(partySlot: Int) {
if (carried.isEmpty) return
val pokemon = serverParty?.get(partySlot) ?: return
val item = carried.item as? PokemonSelectingItem ?: return
val player = playerInventory.player as? ServerPlayer ?: return
if (player.isInBattle()) return
val interaction = item.applyToPokemon(player, carried, pokemon)
if (interaction?.result == InteractionResult.SUCCESS) {
CobblemonCriteria.POKEMON_INTERACT.trigger(player, PokemonInteractContext(pokemon.species.resourceIdentifier, BuiltInRegistries.ITEM.getKey(carried.item)))
broadcastChanges()
}
}
private fun isBlacklisted(stack: ItemStack): Boolean =
BuiltInRegistries.ITEM.getTagOrEmpty(CobblemonItemTags.BLACKLISTED_ITEMS_TO_HOLD).any()
&& stack.`is`(CobblemonItemTags.BLACKLISTED_ITEMS_TO_HOLD)
private fun isWhitelisted(stack: ItemStack): Boolean =
BuiltInRegistries.ITEM.getTagOrEmpty(CobblemonItemTags.WHITELISTED_ITEMS_TO_HOLD).none()
|| stack.`is`(CobblemonItemTags.WHITELISTED_ITEMS_TO_HOLD)
override fun quickMoveStack(player: Player, index: Int): ItemStack { // This will only be called by base inventory
if (index < 0 || index >= slots.size) return ItemStack.EMPTY
val slot = slots[index]
val itemStack = slot.item
val (categoryId, allowedTransfer) = calculateInsertCount(itemStack)
if (allowedTransfer <= 0) return ItemStack.EMPTY
val transferCount = min(itemStack.count, allowedTransfer)
if (slot.hasItem()) {
bagContainer.items.forEach { slotStack ->
if (ItemStack.isSameItemSameComponents(slotStack, itemStack)) {
slotStack.grow(transferCount)
itemStack.shrink(transferCount)
categoryItemCounts[categoryId] += transferCount
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
setupSlotsBasedOnContent()
return ItemStack.EMPTY
}
}
bagContainer.items.add(itemStack.split(transferCount))
categoryItemCounts[categoryId] += transferCount
playSound(CobblemonSounds.ITEM_BAG_ITEM_IN)
setupSlotsBasedOnContent()
}
return ItemStack.EMPTY
}
fun quickMoveStackFromBag(player: Player, slot: CobblemonBagSlot) { //Called by bag items
val transferStack = slot.item
val startingCount = transferStack.count
if (moveItemStackTo(transferStack, INVENTORY_SLOTS.first, HOTBAR_SLOTS.last + 1, false)) {
val itemsMoved = startingCount - transferStack.count
getAndReduceCategoryCount(slot.remove(itemsMoved))
playSound(CobblemonSounds.ITEM_BAG_ITEM_OUT)
setupSlotsBasedOnContent()
}
}
fun calculateInsertCount(stack: ItemStack): Pair<Int, Int> {
if (stack.isEmpty) return -1 to 0
BagCategories.getOrderedCategoriesList().forEachIndexed { index, category ->
if (category.itemIsValid(stack)) {
val maxAllowed = max(category.maxCount - categoryItemCounts[index], 0)
return index to min(stack.count, maxAllowed)
}
}
return -1 to 0
}
fun getAndReduceCategoryCount(stack: ItemStack) {
if (stack.isEmpty) return
BagCategories.getOrderedCategoriesList().forEachIndexed { index, category ->
if (category.itemIsValid(stack)) {
categoryItemCounts[index] -= stack.count
return
}
}
}
override fun canTakeItemForPickAll(stack: ItemStack, slot: Slot): Boolean {
return slot.index !in BAG_SLOTS
}
fun playSound(soundEvent: SoundEvent) {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1.0F))
}
}
@@ -0,0 +1,37 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.bag
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.SimpleContainer
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
class CobblemonBagPartySlot(
container: SimpleContainer,
index: Int,
val activeGetter: () -> Boolean,
val itemGetter: () -> ItemStack,
x: Int,
y: Int
): Slot(container, index, x, y) {
override fun getMaxStackSize(): Int = 1
override fun mayPlace(stack: ItemStack): Boolean = false
override fun mayPickup(player: Player): Boolean = false
override fun getItem(): ItemStack {
return itemGetter.invoke()
}
override fun isActive(): Boolean {
return activeGetter.invoke()
}
}
@@ -0,0 +1,41 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.bag
import net.minecraft.world.entity.player.Player
import net.minecraft.world.inventory.Slot
import net.minecraft.world.item.ItemStack
import java.util.Optional
import kotlin.math.min
class CobblemonBagSlot(
container: CobblemonBagContainer,
var simulatedSlot: Int, //Cant override base slot variable so it is forever -1
x: Int,
y: Int
): Slot(container, 0, x, y) {
var slotOverflowValue = 0
override fun set(stack: ItemStack) {}
override fun getItem(): ItemStack {
if (simulatedSlot == -1) return ItemStack.EMPTY
val itemStack = container.getItem(simulatedSlot)
val count = min(itemStack.count - itemStack.maxStackSize * slotOverflowValue, itemStack.maxStackSize)
return itemStack.copyWithCount(count)
}
override fun remove(amount: Int): ItemStack {
if (simulatedSlot == -1) return ItemStack.EMPTY
return container.removeItem(simulatedSlot, amount)
}
override fun mayPlace(stack: ItemStack): Boolean = false
override fun mayPickup(player: Player): Boolean = false
}
@@ -54,16 +54,14 @@ class FriendshipRaisingBerryItem(block: BerryBlock, val stat: Stat) : BerryItem(
val evLowerAmount = max(genericRuntime.resolveInt(CobblemonMechanics.berries.evLowerAmount), 0)
val decreasedEVs = pokemon.evs.add(stat, -evLowerAmount, ItemEvSource(player, stack, pokemon)) != 0
if (!increasedFriendship && !decreasedEVs) {
return InteractionResultHolder.pass(stack)
}
return if (increasedFriendship || decreasedEVs) {
pokemon.feedPokemon(1)
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
stack.consume(1, player)
InteractionResultHolder.success(stack)
} else {
InteractionResultHolder.pass(stack)
}
stack.consume(1, player)
return InteractionResultHolder.success(stack)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -54,10 +54,7 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
pokemon.currentHealth = Integer.min(pokemon.currentHealth + genericRuntime.resolveInt(amount(), pokemon), pokemon.maxHealth)
stack.consume(1, player)
return InteractionResultHolder.success(stack)
@@ -65,7 +62,7 @@ class HealingBerryItem(block: BerryBlock, val amount: () -> ExpressionLike): Ber
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -48,26 +48,21 @@ class PPRestoringBerryItem(block: BerryBlock, val amount: () -> ExpressionLike):
&& super.canUseOnPokemon(stack, pokemon)
override fun applyToPokemon(player: ServerPlayer, stack: ItemStack, pokemon: Pokemon, move: Move) {
if (!canUseOnPokemon(stack, pokemon) && canUseOnMove(stack, move)) {
return
}
if (canUseOnPokemon(stack, pokemon) && canUseOnMove(stack, move)) {
pokemon.feedPokemon(1)
val moveToRecover = pokemon.moveSet.find { it.template == move.template }
if (moveToRecover != null && moveToRecover.currentPp < moveToRecover.maxPp) {
moveToRecover.currentPp =
min(moveToRecover.maxPp, moveToRecover.currentPp + genericRuntime.resolveInt(amount(), pokemon))
if (!pokemon.feedPokemon(1)) {
return
}
val moveToRecover = pokemon.moveSet.find { it.template == move.template }
if (moveToRecover != null && moveToRecover.currentPp < moveToRecover.maxPp) {
moveToRecover.currentPp =
min(moveToRecover.maxPp, moveToRecover.currentPp + genericRuntime.resolveInt(amount(), pokemon))
stack.consume(1, player)
stack.consume(1, player)
}
}
}
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon, move: Move) {
super.applyToBattlePokemon(player, stack, battlePokemon, move)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -59,9 +59,7 @@ class PortionHealingBerryItem(block: BerryBlock, val canCauseConfusion: Boolean,
}
// count these berries as multiple feedings due to the amount they heal
if (!pokemon.feedPokemon(5)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(5)
var amount = Integer.min(pokemon.currentHealth + (genericRuntime.resolveFloat(portion(), pokemon) * pokemon.maxHealth).toInt(), pokemon.maxHealth)
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(pokemon, amount, this), { cancelledEvent -> return InteractionResultHolder.fail(stack)}) { event ->
@@ -75,7 +73,7 @@ class PortionHealingBerryItem(block: BerryBlock, val canCauseConfusion: Boolean,
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(5, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(5)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -58,23 +58,19 @@ class StatusCuringBerryItem(block: BerryBlock, vararg val status: Status): Berry
stack: ItemStack,
pokemon: Pokemon
): InteractionResultHolder<ItemStack>? {
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
return if (canUseOnPokemon(stack, pokemon)) {
pokemon.feedPokemon(1)
pokemon.status = null
stack.consume(1, player)
InteractionResultHolder.success(stack)
} else {
InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.status = null
stack.consume(1, player)
return InteractionResultHolder.success(stack)
}
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
}
override fun use(world: Level, user: Player, hand: InteractionHand): InteractionResultHolder<ItemStack> {
@@ -945,6 +945,8 @@ object CobblemonItemGroups {
CobblemonItems.pokeBalls.forEach(entries::accept)
CobblemonItems.pokeRods.forEach(entries::accept)
CobblemonItems.pokedexes.forEach(entries::accept)
entries.accept(CobblemonItems.TRAINER_PACK)
entries.accept(CobblemonItems.TRAINER_BAG)
}
private fun blocksInjections(injector: Injector) {
@@ -53,10 +53,7 @@ class BerryJuiceItem : CobblemonItem(Properties()), PokemonSelectingItem, Healin
if (!canUseOnPokemon(stack, pokemon)) {
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
var amount = Integer.min(pokemon.currentHealth + 20, pokemon.maxHealth)
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(pokemon, amount, this), { cancelledEvent -> return InteractionResultHolder.fail(stack)}) { event ->
@@ -77,9 +74,7 @@ class BerryJuiceItem : CobblemonItem(Properties()), PokemonSelectingItem, Healin
override fun applyToBattlePokemon(player: ServerPlayer, stack: ItemStack, battlePokemon: BattlePokemon) {
super.applyToBattlePokemon(player, stack, battlePokemon)
battlePokemon.originalPokemon.feedPokemon(1, playMoodParticle = false)
battlePokemon.originalPokemon.feedPokemon(1)
if (!player.hasInfiniteMaterials()) {
val woodenBowlItemStack = ItemStack(Items.BOWL)
if (!player.inventory.add(woodenBowlItemStack)) {
@@ -33,9 +33,7 @@ class FreshStartMochiItem : CobblemonItem(Properties()), PokemonSelectingItem {
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
pokemon.evs.forEach {
pokemon.setEV(it.key, 0)
@@ -27,9 +27,7 @@ class MochiItem(override val stat: Stat): CobblemonItem(Properties()), EVIncreas
return InteractionResultHolder.fail(stack)
}
if (!pokemon.feedPokemon(1)) {
return InteractionResultHolder.fail(stack)
}
pokemon.feedPokemon(1)
return super.applyToPokemon(player, stack, pokemon)
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.item.interactive
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.item.components.FoodComponent
import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundEvents
@@ -23,10 +24,15 @@ import net.minecraft.world.level.Level
class PonigiriItem : Item(
Properties().stacksTo(64)
.food(FoodProperties.Builder()
.nutrition(2)
.saturationModifier(0.55f)
.nutrition(NUTRITION)
.saturationModifier(SATURATION_MODIFIER)
.build())
) {
companion object {
const val NUTRITION = 2
const val SATURATION_MODIFIER = 0.55f
}
override fun getName(stack: ItemStack): Component {
val ingredients = stack.get(CobblemonItemComponents.INGREDIENT)
@@ -59,4 +65,9 @@ class PonigiriItem : Item(
override fun getEatingSound(): SoundEvent {
return SoundEvents.GENERIC_EAT
}
}
fun getFoodValues(stack: ItemStack): Pair<Int, Float> {
val (hunger, saturation) = stack.getOrDefault(CobblemonItemComponents.FOOD, FoodComponent(NUTRITION, SATURATION_MODIFIER))
return hunger to saturation
}
}
@@ -0,0 +1,28 @@
/*
* 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.net.messages.client.bag
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class UpdateBagMenuDataPacket(val categoryId: Int, val scroll: Int): NetworkPacket<UpdateBagMenuDataPacket> {
companion object {
val ID = cobblemonResource("update_bag_menu_data")
fun decode(buffer: RegistryFriendlyByteBuf): UpdateBagMenuDataPacket {
return UpdateBagMenuDataPacket(buffer.readInt(), buffer.readInt())
}
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeInt(categoryId)
buffer.writeInt(scroll)
}
}
@@ -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.net.messages.client.bag
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class UseItemOnPokemonFromBagPacket(val partySlot: Int): NetworkPacket<UseItemOnPokemonFromBagPacket> {
companion object {
val ID = cobblemonResource("use_item_on_pokemon_from_bag")
fun decode(buffer: RegistryFriendlyByteBuf): UseItemOnPokemonFromBagPacket {
return UseItemOnPokemonFromBagPacket(buffer.readInt())
}
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeInt(partySlot)
}
}
@@ -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.net.messages.client.data
import com.cobblemon.mod.common.api.bag.BagCategories
import com.cobblemon.mod.common.api.bag.BagCategory
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
class BagCategoriesSyncPacket(categories: List<BagCategory>): DataRegistrySyncPacket<BagCategory, BagCategoriesSyncPacket>(categories) {
companion object {
val ID = cobblemonResource("bag_categories")
fun decode(buffer: RegistryFriendlyByteBuf) = BagCategoriesSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: BagCategory) {
entry.encode(buffer)
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): BagCategory = BagCategory.decode(buffer)
override fun synchronizeDecoded(entries: Collection<BagCategory>) {
BagCategories.reload(entries.associateBy { it.identifier })
}
}
@@ -23,16 +23,19 @@ import java.util.UUID
* @author Hiroku
* @since March 5th, 2023
*/
class TradeCompletedPacket(val pokemonId1: UUID, val pokemonId2: UUID) : NetworkPacket<TradeCompletedPacket> {
class TradeCompletedPacket(val pokemonIds1: List<UUID>, val pokemonIds2: List<UUID>) : NetworkPacket<TradeCompletedPacket> {
companion object {
val ID = cobblemonResource("trade_completed")
fun decode(buffer: RegistryFriendlyByteBuf) = TradeCompletedPacket(buffer.readUUID(), buffer.readUUID())
fun decode(buffer: RegistryFriendlyByteBuf) = TradeCompletedPacket(
buffer.readList { it.readUUID() },
buffer.readList { it.readUUID() }
)
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pokemonId1)
buffer.writeUUID(pokemonId2)
buffer.writeCollection(pokemonIds1) { buf, id -> buf.writeUUID(id) }
buffer.writeCollection(pokemonIds2) { buf, id -> buf.writeUUID(id) }
}
}
@@ -12,10 +12,6 @@ import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.net.UnsplittablePacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readNullable
import com.cobblemon.mod.common.util.readUUID
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writeUUID
import net.minecraft.network.RegistryFriendlyByteBuf
import java.util.UUID
@@ -27,15 +23,18 @@ import java.util.UUID
* @author Hiroku
* @since March 5th, 2023
*/
class TradeUpdatedPacket(val playerId: UUID, val pokemon: Pokemon?) : NetworkPacket<TradeUpdatedPacket>, UnsplittablePacket {
class TradeUpdatedPacket(val playerId: UUID, val pokemons: List<Pokemon>) : NetworkPacket<TradeUpdatedPacket>, UnsplittablePacket {
companion object {
val ID = cobblemonResource("trade_updated")
fun decode(buffer: RegistryFriendlyByteBuf) = TradeUpdatedPacket(buffer.readUUID(), buffer.readNullable { Pokemon.S2C_CODEC.decode(buffer) })
fun decode(buffer: RegistryFriendlyByteBuf) = TradeUpdatedPacket(
buffer.readUUID(),
buffer.readList { Pokemon.S2C_CODEC.decode(it as RegistryFriendlyByteBuf) }
)
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(playerId)
buffer.writeNullable(pokemon) { _, pokemon -> Pokemon.S2C_CODEC.encode(buffer, pokemon) }
buffer.writeCollection(pokemons) { buf, pokemon -> Pokemon.S2C_CODEC.encode(buf as RegistryFriendlyByteBuf, pokemon) }
}
}
@@ -11,26 +11,30 @@ package com.cobblemon.mod.common.net.messages.server.trade
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readList
import com.cobblemon.mod.common.util.readNullable
import com.cobblemon.mod.common.util.readPartyPosition
import com.cobblemon.mod.common.util.readUUID
import com.cobblemon.mod.common.util.writeCollection
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writePartyPosition
import com.cobblemon.mod.common.util.writeUUID
import net.minecraft.network.RegistryFriendlyByteBuf
import java.util.UUID
class UpdateTradeOfferPacket(val newOffer: Pair<UUID, PartyPosition>?): NetworkPacket<UpdateTradeOfferPacket> {
class UpdateTradeOfferPacket(val newOffers: List<Pair<UUID, PartyPosition>>): NetworkPacket<UpdateTradeOfferPacket> {
companion object {
val ID = cobblemonResource("update_trade_offer")
fun decode(buffer: RegistryFriendlyByteBuf) = UpdateTradeOfferPacket(buffer.readNullable { buffer.readUUID() to buffer.readPartyPosition() })
fun decode(buffer: RegistryFriendlyByteBuf) = UpdateTradeOfferPacket(
buffer.readList { it.readUUID() to it.readPartyPosition() }
)
}
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeNullable(newOffer) { buffer, (pokemonId, partyPosition) ->
buffer.writeUUID(pokemonId)
buffer.writePartyPosition(partyPosition)
buffer.writeCollection(newOffers) { buf, (pokemonId, partyPosition) ->
buf.writeUUID(pokemonId)
buf.writePartyPosition(partyPosition)
}
}
}
@@ -20,7 +20,7 @@ object ChangeTradeAcceptanceHandler : ServerNetworkPacketHandler<ChangeTradeAcce
override fun handle(packet: ChangeTradeAcceptancePacket, server: MinecraftServer, player: ServerPlayer) {
val trade = TradeManager.getActiveTrade(player.uuid) ?: return player.sendPacket(TradeCancelledPacket())
val tradeParticipant = trade.getTradeParticipant(player.uuid)
if (trade.getOpposingOffer(tradeParticipant).pokemon?.uuid == packet.pokemonOfferId) {
if (trade.getOpposingOffer(tradeParticipant).pokemon.any { it.uuid == packet.pokemonOfferId }) {
trade.updateAcceptance(tradeParticipant, packet.newAcceptance)
}
}
@@ -20,7 +20,7 @@ object PerformTradeHandler : ServerNetworkPacketHandler<PerformTradePacket> {
override fun handle(packet: PerformTradePacket, server: MinecraftServer, player: ServerPlayer) {
val trade = TradeManager.getActiveTrade(player.uuid) ?: return player.sendPacket(TradeCancelledPacket())
val tradeParticipant = trade.getTradeParticipant(player.uuid)
if (trade.getOpposingOffer(tradeParticipant).pokemon?.uuid == packet.pokemonOfferId) {
if (trade.getOpposingOffer(tradeParticipant).pokemon.any { it.uuid == packet.pokemonOfferId }) {
trade.performTrade(tradeParticipant)
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.net.messages.server.trade.CancelTradePacket
import com.cobblemon.mod.common.net.messages.server.trade.UpdateTradeOfferPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.trade.TradeManager
import com.cobblemon.mod.common.util.party
import net.minecraft.server.MinecraftServer
@@ -21,20 +22,16 @@ object UpdateTradeOfferHandler : ServerNetworkPacketHandler<UpdateTradeOfferPack
override fun handle(packet: UpdateTradeOfferPacket, server: MinecraftServer, player: ServerPlayer) {
val trade = TradeManager.getActiveTrade(player.uuid) ?: return player.sendPacket(CancelTradePacket())
val tradeParticipant = trade.getTradeParticipant(player.uuid)
val newOffer = packet.newOffer
if (newOffer == null) {
trade.updateOffer(tradeParticipant, null)
} else {
val (pokemonId, partyPosition) = newOffer
val party = player.party()
val newOffers = packet.newOffers
val validPokemons = mutableListOf<Pokemon>()
val party = player.party()
for ((pokemonId, partyPosition) in newOffers) {
val pokemon = party[partyPosition]
if (pokemon == null || pokemon.uuid != pokemonId) {
return
} else if (!pokemon.tradeable) {
return
} else {
trade.updateOffer(tradeParticipant, pokemon)
if (pokemon != null && pokemon.uuid == pokemonId && pokemon.tradeable) {
validPokemons.add(pokemon)
}
}
trade.updateOffer(tradeParticipant, validPokemons)
}
}
@@ -991,10 +991,6 @@ open class Pokemon : ShowdownIdentifiable {
this.recall()
}
fun playMoodParticle(particleName: String) {
entity?.playMoodParticle(particleName)
}
fun heal() {
CobblemonEvents.POKEMON_HEALED.postThen(PokemonHealedEvent(this)) { event ->
this.currentHealth = maxHealth
@@ -1066,13 +1062,23 @@ open class Pokemon : ShowdownIdentifiable {
else -> 0 // For weights less than 0.1
}
fun feedPokemon(feedCount: Int, playSound: Boolean = true, playMoodParticle: Boolean = true): Boolean {
playFullnessEffects(playSound, playMoodParticle)
fun feedPokemon(feedCount: Int, playSound: Boolean = true) {
// play sounds from the entity first (itemstack is consumed outside of this function)
if (this.entity != null && playSound) {
val fullnessPercent = ((this.currentFullness).toFloat() / (this.getMaxFullness()).toFloat()) * (.5f)
if (this.currentFullness >= this.getMaxFullness()) {
this.entity?.playSound(CobblemonSounds.BERRY_EAT_FULL, 1F, 1F)
}
else {
this.entity?.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F + fullnessPercent)
}
}
// if it is already full we don't need to do anything
// TODO this can happen when feeding in battle since we are temporarily ignoring fullness checks for pp/hp/status berries
if (isFull()) {
return false
return
}
this.currentFullness = (this.currentFullness + feedCount).coerceIn(0, this.getMaxFullness())
@@ -1081,31 +1087,6 @@ open class Pokemon : ShowdownIdentifiable {
if (this.currentFullness == 1) {
this.resetMetabolismCycle()
}
return true
}
private fun playFullnessEffects(playSound: Boolean = true, playMoodParticle: Boolean = true) {
if (!playSound && !playMoodParticle) return
val pokemonEntity = entity ?: return
val fullnessPercent = ((this.currentFullness).toFloat() / (this.getMaxFullness()).toFloat()) * (.5f)
if (!isFull()) {
if (playSound) {
pokemonEntity.playSound(CobblemonSounds.BERRY_EAT, 1F, 1F + fullnessPercent)
}
return
}
if (playSound) {
pokemonEntity.playSound(CobblemonSounds.BERRY_EAT_FULL, 1F, 1F)
if (playMoodParticle) {
pokemonEntity.playMoodParticle("mood_annoyed")
}
}
}
// decrease a pokemon's Fullness value by a certain amount
@@ -173,6 +173,7 @@ class Species : ShowdownIdentifiable {
.addStandardFunctions()
.addSpeciesFunctions(this)
var silenceStepSounds = false
fun initialize() {
Cobblemon.statProvider.provide(this)
@@ -256,6 +257,7 @@ class Species : ShowdownIdentifiable {
}
this.riding.encode(buffer)
buffer.writeBoolean(this.silenceStepSounds)
}
fun decode(buffer: RegistryFriendlyByteBuf) {
@@ -299,6 +301,7 @@ class Species : ShowdownIdentifiable {
}
}
this.riding = RidingProperties.decode(buffer)
this.silenceStepSounds = buffer.readBoolean()
this.initialize()
}
@@ -19,7 +19,7 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
fun getOffer(tradeParticipant: TradeParticipant) = if (tradeParticipant == player1) player1Offer else player2Offer
fun getOpposingOffer(tradeParticipant: TradeParticipant) = if (tradeParticipant == player1) player2Offer else player1Offer
fun updateOffer(tradeParticipant: TradeParticipant, pokemon: Pokemon?) {
fun updateOffer(tradeParticipant: TradeParticipant, pokemon: List<Pokemon>) {
getOffer(tradeParticipant).updateOffer(pokemon)
getOffer(getOppositePlayer(tradeParticipant)).accepted = false
player1.updateOffer(this, tradeParticipant, pokemon)
@@ -30,8 +30,9 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
val offer = getOpposingOffer(tradeParticipant)
if (offer.accepted != acceptance) {
offer.accepted = acceptance
getOppositePlayer(tradeParticipant).changeTradeAcceptance(this, offer.pokemon!!.uuid, acceptance)
tradeParticipant.changeTradeAcceptance(this, offer.pokemon!!.uuid, acceptance)
val firstId = offer.pokemon.firstOrNull()?.uuid ?: java.util.UUID.randomUUID()
getOppositePlayer(tradeParticipant).changeTradeAcceptance(this, firstId, acceptance)
tradeParticipant.changeTradeAcceptance(this, firstId, acceptance)
}
if (offer.accepted && getOffer(tradeParticipant).accepted) {
@@ -51,8 +52,8 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
activeTrade = this,
player1 = player1,
player2 = player2,
pokemon1 = player1Offer.pokemon!!,
pokemon2 = player2Offer.pokemon!!
pokemon1 = player1Offer.pokemon,
pokemon2 = player2Offer.pokemon
)
}
}
@@ -64,12 +65,14 @@ class ActiveTrade(val player1: TradeParticipant, val player2: TradeParticipant)
}
fun completeTrade() {
player1.completeTrade(this, player1Offer.pokemon!!.uuid, player2Offer.pokemon!!.uuid)
player2.completeTrade(this, player2Offer.pokemon!!.uuid, player1Offer.pokemon!!.uuid)
val uuids1 = player1Offer.pokemon.map { it.uuid }
val uuids2 = player2Offer.pokemon.map { it.uuid }
player1.completeTrade(this, uuids1, uuids2)
player2.completeTrade(this, uuids2, uuids1)
player1Offer.pokemon = null
player1Offer.pokemon = emptyList()
player1Offer.accepted = false
player2Offer.pokemon = null
player2Offer.pokemon = emptyList()
player2Offer.accepted = false
}
}
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokemon.TradeEvent
import com.cobblemon.mod.common.api.interaction.ServerPlayerActionRequest
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.api.text.aqua
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferExpiredPacket
import com.cobblemon.mod.common.net.messages.client.trade.TradeOfferNotificationPacket
@@ -101,71 +102,98 @@ object TradeManager : RequestManager<TradeManager.TradeRequest>() {
fun performTrade(
activeTrade: ActiveTrade,
player1: TradeParticipant,
pokemon1: Pokemon,
pokemon1: List<Pokemon>,
player2: TradeParticipant,
pokemon2: Pokemon
pokemon2: List<Pokemon>
) {
val party1 = player1.party
val party2 = player2.party
if (pokemon1 !in party1 || pokemon2 !in party2) {
if (pokemon1.any { it !in party1 } || pokemon2.any { it !in party2 }) {
Cobblemon.LOGGER.warn("Attempted to perform a trade with Pokémon that aren't in the party anymore. Could be attempted duping between ${player1.name} and ${player2.name}")
activeTrade.cancelTrade()
return
}
CobblemonEvents.TRADE_EVENT_PRE.postThen(TradeEvent.Pre(player1, pokemon2, player2, pokemon1)) {
val party1 = player1.party
val party2 = player2.party
if (pokemon1.isEmpty() || pokemon2.isEmpty()) {
Cobblemon.LOGGER.warn("Attempted to perform a trade where one side offered no Pokémon between ${player1.name} and ${player2.name}. This should never happen.")
activeTrade.cancelTrade()
return
}
val player1Removed = party1.remove(pokemon1)
val player2Removed = party2.remove(pokemon2)
CobblemonEvents.TRADE_EVENT_PRE.postThen(
TradeEvent.Pre(player1, pokemon2, player2, pokemon1)
) {
val player1Removed = pokemon1.filter { party1.remove(it) }
val player2Removed = pokemon2.filter { party2.remove(it) }
if (!player1Removed || !player2Removed) {
if (!player1Removed) {
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${pokemon1.species.resourceIdentifier} from ${player1.name.string}(${player1.uuid}). Aborting trade with ${player2.name.string}(${player2.uuid}), potential exploit abused.")
if (player1Removed.size != pokemon1.size || player2Removed.size != pokemon2.size) {
(pokemon1 - player1Removed.toSet()).forEach {
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${it.species.resourceIdentifier} from ${player1.name.string}(${player1.uuid}). Aborting trade with ${player2.name.string}(${player2.uuid}), potential exploit abused.")
}
if (!player2Removed) {
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${pokemon2.species.resourceIdentifier} from ${player2.name.string}(${player2.uuid}). Aborting trade with ${player1.name.string}(${player1.uuid}), potential exploit abused.")
(pokemon2 - player2Removed.toSet()).forEach {
Cobblemon.LOGGER.warn("Failed to remove traded Pokémon ${it.species.resourceIdentifier} from ${player2.name.string}(${player2.uuid}). Aborting trade with ${player1.name.string}(${player1.uuid}), potential exploit abused.")
}
// Revert any changes if one of the removals failed
if (player1Removed) party1.add(pokemon1)
if (player2Removed) party2.add(pokemon2)
player1Removed.forEach { party1.add(it) }
player2Removed.forEach { party2.add(it) }
return@postThen
}
// If player1 is the OT, keep track of the friendship
pokemon1.cacheFriendship(player1.uuid)
pokemon1.forEach { it.cacheFriendship(player1.uuid) }
// If player2 is the OT, keep track of the friendship
pokemon2.cacheFriendship(player2.uuid)
pokemon2.forEach { it.cacheFriendship(player2.uuid) }
// If pokemon2 was originally player1's, set the friendship back to what it was at point of trading
if ( !pokemon2.restoreFriendship(player1.uuid) ) {
// Otherwise, reset the friendship
pokemon2.setFriendship(pokemon2.form.baseFriendship)
// If a Pokémon received by player1 was originally theirs, set the friendship back to what it was at point of trading
pokemon2.forEach {
if (!it.restoreFriendship(player1.uuid)) {
// Otherwise, reset the friendship
it.setFriendship(it.form.baseFriendship)
}
}
// If pokemon1 was originally player2's, set the friendship back to what it was at point of trading
if ( !pokemon1.restoreFriendship(player2.uuid) ) {
// Otherwise, reset the friendship
pokemon1.setFriendship(pokemon1.form.baseFriendship)
// If a Pokémon received by player2 was originally theirs, set the friendship back to what it was at point of trading
pokemon1.forEach {
if (!it.restoreFriendship(player2.uuid)) {
// Otherwise, reset the friendship
it.setFriendship(it.form.baseFriendship)
}
}
party2.add(pokemon1)
party1.add(pokemon2)
pokemon1.forEach { party2.add(it) }
pokemon2.forEach { party1.add(it) }
pokemon1.lockedEvolutions.filterIsInstance<TradeEvolution>().firstOrNull {
it.attemptEvolution(pokemon1, pokemon2)
}
// Each traded Pokémon may evolve if any of the Pokémon it was traded for satisfies its context.
// Evolutions are resolved before any are applied so one evolving can't invalidate another's context.
(pokemon1.map { it to pokemon2 } + pokemon2.map { it to pokemon1 })
.mapNotNull { (pokemon, tradedFor) ->
pokemon.lockedEvolutions.filterIsInstance<TradeEvolution>()
.firstOrNull { evolution -> tradedFor.any { evolution.testContext(pokemon, it) } && evolution.test(pokemon) }
?.let { pokemon to it }
}
.forEach { (pokemon, evolution) -> evolution.evolve(pokemon) }
pokemon2.lockedEvolutions.filterIsInstance<TradeEvolution>().firstOrNull {
it.attemptEvolution(pokemon2, pokemon1)
val compactParty = { party: PartyStore ->
var writeIdx = 0
for (readIdx in 0 until party.size()) {
if (party.get(readIdx) != null) {
if (readIdx != writeIdx) {
party.swap(readIdx, writeIdx)
}
writeIdx++
}
}
}
compactParty(party1)
compactParty(party2)
activeTrade.completeTrade()
CobblemonEvents.TRADE_EVENT_POST.post(TradeEvent.Post(player1, pokemon2, player2, pokemon1))
CobblemonEvents.TRADE_EVENT_POST.post(
TradeEvent.Post(player1, pokemon2, player2, pokemon1)
)
}
}
}
@@ -11,10 +11,10 @@ package com.cobblemon.mod.common.trade
import com.cobblemon.mod.common.pokemon.Pokemon
class TradeOffer {
var pokemon: Pokemon? = null
var pokemon: List<Pokemon> = emptyList()
var accepted = false
fun updateOffer(pokemon: Pokemon?) {
fun updateOffer(pokemon: List<Pokemon>) {
this.pokemon = pokemon
accepted = false
}
@@ -34,7 +34,7 @@ interface TradeParticipant {
*
* If it's null, it means they're no longer offering a Pokémon (so they're redeciding).
*/
fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: Pokemon?)
fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: List<Pokemon>)
/**
* Notifies the participant that the acceptance state for trading with the opponent [Pokemon] with UUID [pokemonId]
@@ -59,7 +59,7 @@ interface TradeParticipant {
fun cancelTrade(trade: ActiveTrade)
/** Notifies the participant that the trade has been completed. The actual Pokémon have already been exchanged. */
fun completeTrade(trade: ActiveTrade, pokemonId1: UUID, pokemonId2: UUID)
fun completeTrade(trade: ActiveTrade, pokemonIds1: List<UUID>, pokemonIds2: List<UUID>)
}
/**
@@ -73,7 +73,7 @@ class PlayerTradeParticipant(val player: ServerPlayer): TradeParticipant {
override val uuid = player.uuid
override val party = player.party()
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: Pokemon?) {
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: List<Pokemon>) {
player.sendPacket(TradeUpdatedPacket(tradeParticipant.uuid, pokemon))
}
@@ -89,8 +89,8 @@ class PlayerTradeParticipant(val player: ServerPlayer): TradeParticipant {
player.sendPacket(TradeCancelledPacket())
}
override fun completeTrade(trade: ActiveTrade, pokemonId1: UUID, pokemonId2: UUID) {
player.sendPacket(TradeCompletedPacket(pokemonId1, pokemonId2))
override fun completeTrade(trade: ActiveTrade, pokemonIds1: List<UUID>, pokemonIds2: List<UUID>) {
player.sendPacket(TradeCompletedPacket(pokemonIds1, pokemonIds2))
}
}
@@ -109,7 +109,7 @@ class DummyTradeParticipant(val pokemonList: MutableList<Pokemon>) : TradePartic
// Don't need to do any notifying.
}
override fun completeTrade(trade: ActiveTrade, pokemonId1: UUID, pokemonId2: UUID) {
override fun completeTrade(trade: ActiveTrade, pokemonIds1: List<UUID>, pokemonIds2: List<UUID>) {
// Don't need to do any notifying.
}
@@ -121,7 +121,7 @@ class DummyTradeParticipant(val pokemonList: MutableList<Pokemon>) : TradePartic
// React, maybe. Change code and hotswap.
}
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: Pokemon?) {
override fun updateOffer(trade: ActiveTrade, tradeParticipant: TradeParticipant, pokemon: List<Pokemon>) {
// React, maybe. Change code and hotswap.
}
}
@@ -234,11 +234,11 @@ fun Entity.isDusk(): Boolean {
}
fun Entity.isStandingOn(blocks: Set<String>, depth: Int = 2): Boolean {
for (currentDepth in 1..depth) {
val bellowBlockPos = blockPosition().below(currentDepth)
for (currentDepth in 0..depth) {
val bellowBlockPos = blockPosBelowThatAffectsMyMovement.below(currentDepth)
val blockState = level().getBlockState(bellowBlockPos)
if (blockState.isAir || !blockState.isCollisionShapeFullBlock(level(), bellowBlockPos)) continue
if (blockState.isAir) continue
val elementOrTags = ExtraCodecs.TAG_OR_ELEMENT_ID.listOf().decode(JavaOps.INSTANCE, blocks.toList()).result().get().first
elementOrTags.forEach {
@@ -27,5 +27,9 @@ object CobblemonGameRules {
@JvmField
val HEALERS_HEAL_PC: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("healersHealPC", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
@JvmField
val HEALER_SET_SPAWN: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("healersSetSpawn", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
@JvmField
val DO_POKEMON_PREDATION: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("doPokemonPredation", GameRules.Category.MOBS, BooleanRuleInvoker.`cobblemon$create`(true))
@JvmField
val BAG_DROP_ITEMS: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("bagDropItems", GameRules.Category.PLAYER, BooleanRuleInvoker.`cobblemon$create`(false))
}
@@ -1,43 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_anger_steam",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/steam_center_double"
}
},
"components": {
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"offset": [0, "math.clamp((v.entity_radius*1.2),0.48,0.96)*0.75", 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1
},
"minecraft:particle_initial_speed": 0,
"minecraft:particle_motion_dynamic": {},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*1.5),0.9,2.4)", "math.clamp((v.entity_radius*0.5),0.3,0.8)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 48,
"texture_height": 176,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [48, 16],
"step_UV": [0, 16],
"max_frame": 11,
"stretch_to_lifetime": true
}
}
}
}
}
}
@@ -1,64 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_anger_veins",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/vein_red"
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.particle_size = math.clamp((v.entity_radius*0.75),0.15,0.4);"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"direction": ["math.random(-1,1)", 1.5, "math.random(-1,1)"]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1.25
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(-1,2)*90"
},
"minecraft:particle_initial_speed": "v.particle_size*13",
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, 0.75, 0],
"linear_drag_coefficient": "v.particle_size*15"
},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*0.75),0.15,0.3)", "math.clamp((v.entity_radius*0.75),0.15,0.3)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 22,
"texture_height": 11,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [11, 11],
"step_UV": [11, 0],
"frames_per_second": 6,
"max_frame": 2,
"loop": true
}
}
},
"minecraft:particle_motion_collision": {},
"minecraft:particle_appearance_lighting": {},
"minecraft:particle_appearance_tinting": {
"color": {
"interpolant": "v.particle_age/v.particle_lifetime",
"gradient": {
"0.86": "#ffffffff",
"1.0": "#FF000000"
}
}
}
}
}
}
@@ -1,49 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_annoyed",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/scribble_purple"
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.particle_size = math.clamp((v.entity_radius*0.75),0.2,0.5);"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 2
},
"minecraft:emitter_shape_point": {
"direction": [0, 1, 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 1
},
"minecraft:particle_initial_speed": "v.particle_size*10",
"minecraft:particle_motion_dynamic": {
"linear_drag_coefficient": 3
},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*0.75),0.2,0.5)", "math.clamp((v.entity_radius*0.75),0.2,0.5)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 168,
"texture_height": 14,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [14, 14],
"step_UV": [14, 0],
"frames_per_second": 10,
"max_frame": 12
}
}
},
"minecraft:particle_appearance_lighting": {}
}
}
}
@@ -1,47 +0,0 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:mood_stress",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/mood/sweat_blue"
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.particle_height = math.clamp((v.entity_radius*1.2),0.48,0.96);"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_looping": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"offset": [0, "math.clamp((v.entity_radius*1.2),0.48,0.96)*0.75", 0]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.45
},
"minecraft:particle_initial_speed": 0,
"minecraft:particle_motion_dynamic": {},
"minecraft:particle_appearance_billboard": {
"size": ["math.clamp((v.entity_radius*1.2),0.48,0.96)", "math.clamp((v.entity_radius*0.65),0.26,0.52)"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 216,
"texture_height": 13,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [24, 13],
"step_UV": [24, 0],
"max_frame": 9,
"stretch_to_lifetime": true
}
}
},
"minecraft:particle_appearance_lighting": {}
}
}
}
@@ -1815,6 +1815,12 @@
"wings_open": {
"scale": 0
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_claw_medium');",
"0.5": "q.run_molang('cobblemon:stepsound_claw_medium');",
"1.0": "q.run_molang('cobblemon:stepsound_claw_medium');",
"1.5": "q.run_molang('cobblemon:stepsound_claw_medium');"
}
},
"animation.pidgeot.mount_ground_idle": {
@@ -3631,6 +3637,12 @@
"wings_open": {
"scale": 0
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_claw_medium');",
"0.5": "q.run_molang('cobblemon:stepsound_claw_medium');",
"1.0": "q.run_molang('cobblemon:stepsound_claw_medium');",
"1.5": "q.run_molang('cobblemon:stepsound_claw_medium');"
}
},
"animation.pidgeot.ride_ground_walk": {
@@ -1682,6 +1682,12 @@
"1.3333": [10, 9, 0]
}
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_standard_medium');",
"0.4583": "q.run_molang('cobblemon:stepsound_standard_medium');",
"0.6667": "q.run_molang('cobblemon:stepsound_standard_medium');",
"1.125": "q.run_molang('cobblemon:stepsound_standard_medium');"
}
},
"animation.arcanine.ride_ground_walk": {
@@ -2639,6 +2645,12 @@
"chest_fluff_right": {
"rotation": [5, "-22.5+math.cos(q.anim_time*90*8-120)*15", "-math.sin(q.anim_time*90*8-120)*3"]
}
},
"timeline": {
"0.125": "q.run_molang('cobblemon:stepsound_standard_medium');",
"0.2083": "q.run_molang('cobblemon:stepsound_standard_large');",
"0.2917": "q.run_molang('cobblemon:stepsound_standard_medium');",
"0.375": "q.run_molang('cobblemon:stepsound_standard_medium');"
}
},
"animation.arcanine.ride_ground_run": {
@@ -4161,41 +4173,101 @@
"eye_left": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [-0.1, 0, 0.1]},
"0.5": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [-0.1, 0, 0.1]},
"1.6667": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [-0.1, 0, 0.1]
},
"0.5": {
"pre": [-0.1, 0, 0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0, 0.1]
},
"1.6667": {
"pre": [-0.1, 0, 0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [0.1, 0, 0.1]},
"0.5": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [0.1, 0, 0.1]},
"1.6667": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [0.1, 0, 0.1]
},
"0.5": {
"pre": [0.1, 0, 0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0, 0.1]
},
"1.6667": {
"pre": [0.1, 0, 0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"0.5": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"1.6667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"0.5": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"1.6667": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"0.5": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"1.6667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"0.5": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"1.6667": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"jaw": {
@@ -4823,41 +4895,101 @@
"eye_left": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [-0.1, 0, 0.1]},
"0.5": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [-0.1, 0, 0.1]},
"1.6667": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [-0.1, 0, 0.1]
},
"0.5": {
"pre": [-0.1, 0, 0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0, 0.1]
},
"1.6667": {
"pre": [-0.1, 0, 0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [0.1, 0, 0.1]},
"0.5": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [0.1, 0, 0.1]},
"1.6667": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [0.1, 0, 0.1]
},
"0.5": {
"pre": [0.1, 0, 0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0, 0.1]
},
"1.6667": {
"pre": [0.1, 0, 0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"0.5": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"1.6667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"0.5": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"1.6667": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.1667": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"0.5": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
"1.4167": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"1.6667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
"2.5": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.1667": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"0.5": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
},
"1.4167": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"1.6667": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
},
"2.5": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"jaw": {
@@ -5287,14 +5419,20 @@
"position": {
"0.0": [-0.1, 0, 0.1],
"0.125": [-0.1, 0, 0.1],
"0.1667": {"pre": [-0.1, 0, 0.1], "post": [0, 0, 0]}
"0.1667": {
"pre": [-0.1, 0, 0.1],
"post": [0, 0, 0]
}
}
},
"eye_right": {
"position": {
"0.0": [0.1, 0, 0.1],
"0.125": [0.1, 0, 0.1],
"0.1667": {"pre": [0.1, 0, 0.1], "post": [0, 0, 0]}
"0.1667": {
"pre": [0.1, 0, 0.1],
"post": [0, 0, 0]
}
}
}
}
@@ -5491,47 +5629,95 @@
"eye_left": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [-0.2, 0, 0.1]},
"0.3333": {"pre": [-0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [-0.2, 0, 0.1]
},
"0.3333": {
"pre": [-0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [0.2, 0, 0.1]},
"0.3333": {"pre": [0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [0.2, 0, 0.1]
},
"0.3333": {
"pre": [0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [-0.2, 0, 0.1]},
"0.3333": {"pre": [-0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [-0.2, 0, 0.1]
},
"0.3333": {
"pre": [-0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [0.2, 0, 0.1]},
"0.3333": {"pre": [0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [0.2, 0, 0.1]
},
"0.3333": {
"pre": [0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_left_hurt": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"0.3333": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"0.3333": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_right_hurt": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"0.3333": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"0.3333": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"jaw": {
@@ -6238,47 +6424,95 @@
"eye_left": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [-0.2, 0, 0.1]},
"0.3333": {"pre": [-0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [-0.2, 0, 0.1]
},
"0.3333": {
"pre": [-0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eye_right": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [0.2, 0, 0.1]},
"0.3333": {"pre": [0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [0.2, 0, 0.1]
},
"0.3333": {
"pre": [0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [-0.2, 0, 0.1]},
"0.3333": {"pre": [-0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [-0.2, 0, 0.1]
},
"0.3333": {
"pre": [-0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [0.2, 0, 0.1]},
"0.3333": {"pre": [0.2, 0, 0.1], "post": [0, 0, 0]},
"1.25": {"pre": [0, 0, 0], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [0.2, 0, 0.1]
},
"0.3333": {
"pre": [0.2, 0, 0.1],
"post": [0, 0, 0]
},
"1.25": {
"pre": [0, 0, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_left_hurt": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"0.3333": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"0.3333": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_right_hurt": {
"position": {
"0.0": [0, 0, 0],
"0.0417": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"0.3333": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
"0.0417": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"0.3333": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"jaw": {
@@ -3150,6 +3150,10 @@
"locator_seat_2": {
"rotation": [45, 0, 0]
}
},
"timeline": {
"0.5": "q.run_molang('cobblemon:stepsound_claw_large');",
"1.5": "q.run_molang('cobblemon:stepsound_claw_large');"
}
},
"animation.corviknight.ground_run": {
@@ -4473,6 +4477,10 @@
"locator_seat_2": {
"rotation": [25, 0, 0]
}
},
"timeline": {
"0.3333": "q.run_molang('cobblemon:stepsound_claw_large');",
"0.8333": "q.run_molang('cobblemon:stepsound_claw_large');"
}
},
"animation.corviknight.air_idle": {
@@ -566,6 +566,12 @@
"1.5": [0, 0.2, 0]
}
}
},
"timeline": {
"0.5417": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"0.7083": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"1.2917": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"1.4583": "q.run_molang('cobblemon:stepsound_hoof_medium');"
}
},
"animation.wyrdeer.sleep": {
@@ -780,15 +786,27 @@
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.25": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"0.5": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
"0.25": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"0.5": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.25": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"0.5": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
"0.25": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"0.5": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"ear_right": {
@@ -1186,18 +1204,36 @@
"position": {
"0.0": [0, 0, 0],
"1.5833": [-0.1, 0, -0.1],
"1.6667": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]},
"3.0": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"3.125": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
"1.6667": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
},
"3.0": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"3.125": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"1.5833": [0.1, 0, -0.1],
"1.6667": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]},
"3.0": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"3.125": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
"1.6667": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
},
"3.0": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"3.125": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"tail": {
@@ -2204,6 +2240,16 @@
"1.1667": [0, 0, 0]
}
}
},
"timeline": {
"0.2083": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"0.2917": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"0.375": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"0.4583": "q.run_molang('cobblemon:stepsound_hoof_large');",
"0.7917": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"0.875": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"0.9583": "q.run_molang('cobblemon:stepsound_hoof_medium');",
"1.0417": "q.run_molang('cobblemon:stepsound_hoof_large');"
}
},
"animation.wyrdeer.ride_jump": {
@@ -800,6 +800,8 @@
"block.cobblemon.relic_coin_sack": "Relic Coin Sack",
"item.cobblemon.automaton_armor_trim_smithing_template": "Smithing Template",
"trim_pattern.cobblemon.automaton": "Automaton Armor Trim",
"item.cobblemon.trainer_pack": "Trainer Pack",
"item.cobblemon.trainer_bag": "Trainer Bag",
"cobblemon.tooltip.see_more_info": "Hold shift to see info",
"cobblemon.item_class.fishing_bait": "Fishing Bait",
"cobblemon.item_class.cooking_pot_recipe_absorbs": "Absorbs",
@@ -1198,7 +1200,14 @@
"cobblemon.ui.edit.habitat.validation.phases_list": "Phases must use a valid format for integer ranges, e.g. 1-3 or 1, 3-5, 6.",
"cobblemon.ui.edit.habitat.validation.min_light": "Min Light must be between 0 and 15.",
"cobblemon.ui.edit.habitat.validation.max_light": "Max Light must be between 0 and 15.",
"cobblemon.container.bag": "Trainer Bag",
"cobblemon.bag_category.poke_balls": "Poké Balls",
"cobblemon.bag_category.consumables": "Consumables",
"cobblemon.bag_category.berries": "Berries",
"cobblemon.bag_category.held_evo": "Held and Evolution Items",
"cobblemon.bag_category.tms": "Technical Machines",
"cobblemon.bag_category.currency": "Currency",
"cobblemon.bag_categories.all_items": "Bag",
"cobblemon.ui.pokenav.title": "PokéNav",
"cobblemon.ui.pokenav.narrator.backbutton": "PokéNav button pressed",
@@ -0,0 +1,131 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"texture_size": [64, 64],
"textures": {
"particle": "cobblemon:item/bag/bag_penny",
"texture": "cobblemon:item/bag/bag_penny"
},
"elements": [
{
"name": "straps",
"from": [3.65, 11.65, 5.65],
"to": [12.35, 24.35, 10.35],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
"faces": {
"north": {"uv": [1, 1, 3, 4], "texture": "#texture"},
"east": {"uv": [0, 1, 1, 4], "texture": "#texture"},
"south": {"uv": [4, 1, 6, 4], "texture": "#texture"},
"west": {"uv": [3, 1, 4, 4], "texture": "#texture"},
"up": {"uv": [3, 1, 1, 0], "texture": "#texture"},
"down": {"uv": [5, 0, 3, 1], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [4.51, 19.01, 10.01],
"to": [11.49, 21.99, 12.99],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 19.5, 11.5]},
"faces": {
"north": {"uv": [6.75, 2.5, 8.5, 3.25], "texture": "#texture"},
"east": {"uv": [6, 2.5, 6.75, 3.25], "texture": "#texture"},
"south": {"uv": [9.25, 2.5, 11, 3.25], "texture": "#texture"},
"west": {"uv": [8.5, 2.5, 9.25, 3.25], "texture": "#texture"},
"up": {"uv": [8.5, 2.5, 6.75, 1.75], "texture": "#texture"},
"down": {"uv": [10.25, 1.75, 8.5, 2.5], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [1.99, 21.99, 12.49],
"to": [14.01, 26.01, 12.51],
"rotation": {"angle": 22.5, "axis": "x", "origin": [8, 22, 12.5]},
"faces": {
"north": {"uv": [7.5, 3.75, 10.5, 4.75], "texture": "#texture"},
"east": {"uv": [7.5, 3.75, 7.5, 4.75], "texture": "#texture"},
"south": {"uv": [10.5, 3.75, 13.5, 4.75], "texture": "#texture"},
"west": {"uv": [10.5, 3.75, 10.5, 4.75], "texture": "#texture"},
"up": {"uv": [10.5, 3.75, 7.5, 3.75], "texture": "#texture"},
"down": {"uv": [13.5, 3.75, 10.5, 3.75], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [4.51, 12.51, 10.01],
"to": [11.49, 16.49, 12.99],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 14, 11.5]},
"faces": {
"north": {"uv": [6.75, 0.75, 8.5, 1.75], "texture": "#texture"},
"east": {"uv": [6, 0.75, 6.75, 1.75], "texture": "#texture"},
"south": {"uv": [9.25, 0.75, 11, 1.75], "texture": "#texture"},
"west": {"uv": [8.5, 0.75, 9.25, 1.75], "texture": "#texture"},
"up": {"uv": [8.5, 0.75, 6.75, 0], "texture": "#texture"},
"down": {"uv": [10.25, 0, 8.5, 0.75], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [4.99, 11.49, 11.99],
"to": [11.01, 12.51, 12.01],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 12, 11]},
"faces": {
"north": {"uv": [6, 5, 7.5, 5.25], "texture": "#texture"},
"east": {"uv": [6, 5, 6, 5.25], "texture": "#texture"},
"south": {"uv": [7.5, 5, 9, 5.25], "texture": "#texture"},
"west": {"uv": [7.5, 5, 7.5, 5.25], "texture": "#texture"},
"up": {"uv": [7.5, 5, 6, 5], "texture": "#texture"},
"down": {"uv": [9, 5, 7.5, 5], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [5.99, 8.49, 10.99],
"to": [10.01, 12.51, 11.01],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 10, 10.5]},
"faces": {
"north": {"uv": [6, 5.5, 7, 6.5], "texture": "#texture"},
"east": {"uv": [6, 5.5, 6, 6.5], "texture": "#texture"},
"south": {"uv": [7, 5.5, 8, 6.5], "texture": "#texture"},
"west": {"uv": [7, 5.5, 7, 6.5], "texture": "#texture"},
"up": {"uv": [7, 5.5, 6, 5.5], "texture": "#texture"},
"down": {"uv": [8, 5.5, 7, 5.5], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [4.01, 16.01, 9.51],
"to": [11.99, 18.99, 13.49],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 16.5, 11]},
"faces": {
"north": {"uv": [1, 5, 3, 5.75], "texture": "#texture"},
"east": {"uv": [0, 5, 1, 5.75], "texture": "#texture"},
"south": {"uv": [4, 5, 6, 5.75], "texture": "#texture"},
"west": {"uv": [3, 5, 4, 5.75], "texture": "#texture"},
"up": {"uv": [3, 5, 1, 4], "texture": "#texture"},
"down": {"uv": [5, 4, 3, 5], "texture": "#texture"}
}
},
{
"name": "bag",
"from": [3.8, 15.8, 9.3],
"to": [12.2, 19.2, 13.7],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 16.5, 11]},
"faces": {
"north": {"uv": [1, 6.75, 3, 7.5], "texture": "#texture"},
"east": {"uv": [0, 6.75, 1, 7.5], "texture": "#texture"},
"south": {"uv": [4, 6.75, 6, 7.5], "texture": "#texture"},
"west": {"uv": [3, 6.75, 4, 7.5], "texture": "#texture"},
"up": {"uv": [3, 6.75, 1, 5.75], "texture": "#texture"},
"down": {"uv": [5, 5.75, 3, 6.75], "texture": "#texture"}
}
}
],
"groups": [
{
"name": "bag",
"origin": [0, 0, 0],
"color": 0,
"children": [0, 1, 2, 3, 4, 5, 6, 7]
}
]
}
@@ -0,0 +1,63 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"texture_size": [32, 32],
"textures": {
"0": "cobblemon:item/bag/trainer_bag",
"particle": "cobblemon:item/bag/trainer_bag"
},
"elements": [
{
"name": "straps",
"from": [3.65, 11.65, 5.65],
"to": [12.35, 24.35, 10.35],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
"faces": {
"north": {"uv": [2, 2, 6, 8], "texture": "#0"},
"east": {"uv": [0, 2, 2, 8], "texture": "#0"},
"south": {"uv": [8, 2, 12, 8], "texture": "#0"},
"west": {"uv": [6, 2, 8, 8], "texture": "#0"},
"up": {"uv": [6, 2, 2, 0], "texture": "#0"},
"down": {"uv": [10, 0, 6, 2], "texture": "#0"}
}
},
{
"name": "straps inverted",
"from": [12.35, 24.35, 10.35],
"to": [3.65, 11.65, 5.65],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
"faces": {
"north": {"uv": [8, 8, 12, 2], "texture": "#0"},
"east": {"uv": [6, 8, 8, 2], "texture": "#0"},
"south": {"uv": [2, 8, 6, 2], "texture": "#0"},
"west": {"uv": [0, 8, 2, 2], "texture": "#0"},
"up": {"uv": [6, 0, 10, 2], "texture": "#0"},
"down": {"uv": [2, 2, 6, 0], "texture": "#0"}
}
},
{
"name": "bag",
"from": [4, 14, 10],
"to": [12, 20, 13],
"rotation": {"angle": 22.5, "axis": "z", "origin": [8, 17, 11.5]},
"faces": {
"north": {"uv": [1.5, 9.5, 5.5, 12.5], "texture": "#0"},
"east": {"uv": [0, 9.5, 1.5, 12.5], "texture": "#0"},
"south": {"uv": [7, 9.5, 11, 12.5], "texture": "#0"},
"west": {"uv": [5.5, 9.5, 7, 12.5], "texture": "#0"},
"up": {"uv": [5.5, 9.5, 1.5, 8], "texture": "#0"},
"down": {"uv": [9.5, 8, 5.5, 9.5], "texture": "#0"}
}
}
],
"gui_light": "front",
"groups": [
{
"name": "bag",
"origin": [8, 0, 8],
"scope": 0,
"color": 0,
"children": [0, 1, 2]
}
]
}
@@ -0,0 +1,101 @@
{
"format_version": "1.9.0",
"credit": "Made with Blockbench",
"texture_size": [64, 32],
"textures": {
"0": "cobblemon:item/bag/trainer_pack",
"particle": "cobblemon:item/bag/trainer_pack"
},
"elements": [
{
"name": "straps",
"from": [3.65, 11.65, 5.65],
"to": [12.35, 24.35, 10.35],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
"faces": {
"north": {"uv": [1, 2, 3, 8], "texture": "#0"},
"east": {"uv": [0, 2, 1, 8], "texture": "#0"},
"south": {"uv": [4, 2, 6, 8], "texture": "#0"},
"west": {"uv": [3, 2, 4, 8], "texture": "#0"},
"up": {"uv": [3, 2, 1, 0], "texture": "#0"},
"down": {"uv": [5, 0, 3, 2], "texture": "#0"}
}
},
{
"name": "straps inverted",
"from": [12.35, 24.35, 10.35],
"to": [3.65, 11.65, 5.65],
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
"faces": {
"north": {"uv": [4, 8, 6, 2], "texture": "#0"},
"east": {"uv": [3, 8, 4, 2], "texture": "#0"},
"south": {"uv": [1, 8, 3, 2], "texture": "#0"},
"west": {"uv": [0, 8, 1, 2], "texture": "#0"},
"up": {"uv": [3, 0, 5, 2], "texture": "#0"},
"down": {"uv": [1, 2, 3, 0], "texture": "#0"}
}
},
{
"name": "bag",
"from": [6, 21.5, 11.5],
"to": [10, 23.5, 11.5],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 24, 11.5]},
"faces": {
"north": {"uv": [6.25, 5, 7.25, 6], "texture": "#0"},
"south": {"uv": [7.25, 5, 8.25, 6], "texture": "#0"}
}
},
{
"name": "bag",
"from": [4.01, 16.5, 10],
"to": [11.99, 21.5, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 15.5, 11.5]},
"faces": {
"north": {"uv": [7.25, 2, 9.25, 4.5], "texture": "#0"},
"east": {"uv": [6.25, 2, 7.25, 4.5], "texture": "#0"},
"south": {"uv": [10.25, 2, 12.25, 4.5], "texture": "#0"},
"west": {"uv": [9.25, 2, 10.25, 4.5], "texture": "#0"},
"up": {"uv": [9.25, 2, 7.25, 0], "texture": "#0"},
"down": {"uv": [11.25, 0, 9.25, 2], "texture": "#0"}
}
},
{
"name": "bag",
"from": [3.5, 12.5, 10],
"to": [12.5, 16.5, 14],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 15.5, 11.5]},
"faces": {
"north": {"uv": [1, 14, 3.25, 16], "texture": "#0"},
"east": {"uv": [0, 14, 1, 16], "texture": "#0"},
"south": {"uv": [4.25, 14, 6.5, 16], "texture": "#0"},
"west": {"uv": [3.25, 14, 4.25, 16], "texture": "#0"},
"up": {"uv": [3.25, 14, 1, 12], "texture": "#0"},
"down": {"uv": [5.5, 12, 3.25, 14], "texture": "#0"}
}
},
{
"name": "bag",
"from": [4.5, 12.5, 14],
"to": [11.5, 16.5, 15],
"rotation": {"angle": 0, "axis": "y", "origin": [6.5, 16.5, 15.5]},
"faces": {
"north": {"uv": [0.25, 10, 2, 12], "texture": "#0"},
"east": {"uv": [0, 10, 0.25, 12], "texture": "#0"},
"south": {"uv": [2.25, 10, 4, 12], "texture": "#0"},
"west": {"uv": [2, 10, 2.25, 12], "texture": "#0"},
"up": {"uv": [2, 10, 0.25, 9.5], "texture": "#0"},
"down": {"uv": [3.75, 9.5, 2, 10], "texture": "#0"}
}
}
],
"gui_light": "front",
"groups": [
{
"name": "bag",
"origin": [8, 0, 8],
"scope": 0,
"color": 0,
"children": [0, 1, 2, 3, 4, 5]
}
]
}
@@ -0,0 +1,47 @@
{
"parent": "cobblemon:item/bag/trainer_bag",
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"rotation": [75, -155, 0],
"translation": [2.25, -4, -11],
"scale": [0.75, 0.75, 0.75]
},
"thirdperson_lefthand": {
"rotation": [75, -155, 0],
"translation": [2.25, -4, -11],
"scale": [0.75, 0.75, 0.75]
},
"firstperson_righthand": {
"rotation": [0, -100, 0],
"translation": [5, -7, 0],
"scale": [0.8, 0.8, 0.8]
},
"firstperson_lefthand": {
"rotation": [0, -100, 0],
"translation": [5, -7, 0],
"scale": [0.8, 0.8, 0.8]
},
"ground": {
"translation": [0, -3, -1.75],
"scale": [0.5, 0.5, 0.5]
},
"gui": {
"rotation": [30, -22.5, 0],
"translation": [0.75, -7, 0],
"scale": [0.875, 0.875, 0.875]
},
"head": {
"translation": [0, -32.25, 0],
"scale": [1.6, 1.6, 1.6]
},
"fixed": {
"rotation": [0, -180, 0],
"translation": [0, -9.25, 3.5]
},
"on_shelf": {
"translation": [0, 0, -7.5],
"scale": [1.75, 1.75, 1.75]
}
}
}
@@ -0,0 +1,47 @@
{
"parent": "cobblemon:item/bag/trainer_pack",
"gui_light": "front",
"display": {
"thirdperson_righthand": {
"rotation": [75, -155, 0],
"translation": [2.25, -4, -11],
"scale": [0.75, 0.75, 0.75]
},
"thirdperson_lefthand": {
"rotation": [75, -155, 0],
"translation": [2.25, -4, -11],
"scale": [0.75, 0.75, 0.75]
},
"firstperson_righthand": {
"rotation": [0, -100, 0],
"translation": [5, -7, 0],
"scale": [0.8, 0.8, 0.8]
},
"firstperson_lefthand": {
"rotation": [0, -100, 0],
"translation": [5, -7, 0],
"scale": [0.8, 0.8, 0.8]
},
"ground": {
"translation": [0, -3, -1.75],
"scale": [0.5, 0.5, 0.5]
},
"gui": {
"rotation": [30, -22.5, 0],
"translation": [0.75, -7, 0],
"scale": [0.875, 0.875, 0.875]
},
"head": {
"translation": [0, -32.25, 0],
"scale": [1.6, 1.6, 1.6]
},
"fixed": {
"rotation": [0, -180, 0],
"translation": [0, -9.25, 3.5]
},
"on_shelf": {
"translation": [0, 0, -7.5],
"scale": [1.75, 1.75, 1.75]
}
}
}
@@ -261,6 +261,24 @@
"fishing.bait_detach": {
"sounds": [ { "name": "cobblemon:fishing/fishing_rod_remove_bait", "volume": 0.8 } ]
},
"item.bag.open": {
"sounds": [ { "name": "cobblemon:item/bag/bag_open", "volume": 0.5 } ]
},
"item.bag.close": {
"sounds": [ { "name": "cobblemon:item/bag/bag_close", "volume": 0.5 } ]
},
"item.bag.click": {
"sounds": [ { "name": "cobblemon:item/bag/bag_click", "volume": 0.3 } ]
},
"item.bag.item_in": {
"sounds": [ { "name": "cobblemon:item/bag/bag_item_in", "volume": 0.3 } ]
},
"item.bag.item_out": {
"sounds": [ { "name": "cobblemon:item/bag/bag_item_out", "volume": 0.3 } ]
},
"item.bag.equip": {
"sounds": [ { "name": "cobblemon:item/bag/bag_equip", "volume": 0.4 } ]
},
"block.healing_machine.active": {
"sounds": [ { "name": "cobblemon:block/healing_machine/healing_machine_active", "volume": 0.7 } ]
},
@@ -2031,6 +2049,850 @@
{ "name": "cobblemon:animation/wings/steel_wing_flap_medium_8", "volume": 0.25 }
]
},
"stepsound.dirt.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/dirt/standard/large/stepsound_dirt_standard_large_8", "volume": 0.2 }
]
},
"stepsound.dirt.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/dirt/standard/medium/stepsound_dirt_standard_medium_8", "volume": 0.1 }
]
},
"stepsound.grass.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/grass/standard/large/stepsound_grass_standard_large_8", "volume": 0.2 }
]
},
"stepsound.grass.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/grass/standard/medium/stepsound_grass_standard_medium_8", "volume": 0.1 }
]
},
"stepsound.stone.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/stone/standard/large/stepsound_stone_standard_large_8", "volume": 0.2 }
]
},
"stepsound.stone.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/stone/standard/medium/stepsound_stone_standard_medium_8", "volume": 0.1 }
]
},
"stepsound.sand.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/standard/large/stepsound_sand_standard_large_8", "volume": 0.2 }
]
},
"stepsound.sand.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/standard/medium/stepsound_sand_standard_medium_8", "volume": 0.1 }
]
},
"stepsound.snow.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_1", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_2", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_3", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_4", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_5", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_6", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_7", "volume": 0.18 },
{ "name": "cobblemon:stepsound/snow/standard/large/stepsound_snow_standard_large_8", "volume": 0.18 }
]
},
"stepsound.snow.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_1", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_2", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_3", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_4", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_5", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_6", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_7", "volume": 0.08 },
{ "name": "cobblemon:stepsound/snow/standard/medium/stepsound_snow_standard_medium_8", "volume": 0.08 }
]
},
"stepsound.wood.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_1", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_2", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_3", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_4", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_5", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_6", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_7", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/standard/large/stepsound_wood_standard_large_8", "volume": 0.25 }
]
},
"stepsound.wood.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_1", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_2", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_3", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_4", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_5", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_6", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_7", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/standard/medium/stepsound_wood_standard_medium_8", "volume": 0.15 }
]
},
"stepsound.mud.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/standard/large/stepsound_mud_standard_large_8", "volume": 0.22 }
]
},
"stepsound.mud.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_1", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_2", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_3", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_4", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_5", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_6", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_7", "volume": 0.14 },
{ "name": "cobblemon:stepsound/mud/standard/medium/stepsound_mud_standard_medium_8", "volume": 0.14 }
]
},
"stepsound.glass.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/standard/large/stepsound_glass_standard_large_8", "volume": 0.2 }
]
},
"stepsound.glass.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/standard/medium/stepsound_glass_standard_medium_8", "volume": 0.1 }
]
},
"stepsound.metal.standard.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/standard/large/stepsound_metal_standard_large_8", "volume": 0.2 }
]
},
"stepsound.metal.standard.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/standard/medium/stepsound_metal_standard_medium_8", "volume": 0.1 }
]
},
"stepsound.dirt.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_1", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_2", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_3", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_4", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_5", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_6", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_7", "volume": 0.25 },
{ "name": "cobblemon:stepsound/dirt/hoof/large/stepsound_dirt_hoof_large_8", "volume": 0.25 }
]
},
"stepsound.dirt.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_1", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_2", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_3", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_4", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_5", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_6", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_7", "volume": 0.15 },
{ "name": "cobblemon:stepsound/dirt/hoof/medium/stepsound_dirt_hoof_medium_8", "volume": 0.15 }
]
},
"stepsound.grass.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_1", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_2", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_3", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_4", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_5", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_6", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_7", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/hoof/large/stepsound_grass_hoof_large_8", "volume": 0.25 }
]
},
"stepsound.grass.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_1", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_2", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_3", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_4", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_5", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_6", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_7", "volume": 0.15 },
{ "name": "cobblemon:stepsound/grass/hoof/medium/stepsound_grass_hoof_medium_8", "volume": 0.15 }
]
},
"stepsound.stone.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_1", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_2", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_3", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_4", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_5", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_6", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_7", "volume": 0.25 },
{ "name": "cobblemon:stepsound/stone/hoof/large/stepsound_stone_hoof_large_8", "volume": 0.25 }
]
},
"stepsound.stone.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_1", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_2", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_3", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_4", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_5", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_6", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_7", "volume": 0.15 },
{ "name": "cobblemon:stepsound/stone/hoof/medium/stepsound_stone_hoof_medium_8", "volume": 0.15 }
]
},
"stepsound.sand.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/sand/hoof/large/stepsound_sand_hoof_large_8", "volume": 0.2 }
]
},
"stepsound.sand.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/sand/hoof/medium/stepsound_sand_hoof_medium_8", "volume": 0.1 }
]
},
"stepsound.snow.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/hoof/large/stepsound_snow_hoof_large_8", "volume": 0.22 }
]
},
"stepsound.snow.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_1", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_2", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_3", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_4", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_5", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_6", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_7", "volume": 0.12 },
{ "name": "cobblemon:stepsound/snow/hoof/medium/stepsound_snow_hoof_medium_8", "volume": 0.12 }
]
},
"stepsound.wood.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_1", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_2", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_3", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_4", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_5", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_6", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_7", "volume": 0.25 },
{ "name": "cobblemon:stepsound/wood/hoof/large/stepsound_wood_hoof_large_8", "volume": 0.25 }
]
},
"stepsound.wood.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_1", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_2", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_3", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_4", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_5", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_6", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_7", "volume": 0.15 },
{ "name": "cobblemon:stepsound/wood/hoof/medium/stepsound_wood_hoof_medium_8", "volume": 0.15 }
]
},
"stepsound.mud.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/mud/hoof/large/stepsound_mud_hoof_large_8", "volume": 0.2 }
]
},
"stepsound.mud.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/mud/hoof/medium/stepsound_mud_hoof_medium_8", "volume": 0.1 }
]
},
"stepsound.glass.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/glass/hoof/large/stepsound_glass_hoof_large_8", "volume": 0.2 }
]
},
"stepsound.glass.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/glass/hoof/medium/stepsound_glass_hoof_medium_8", "volume": 0.1 }
]
},
"stepsound.metal.hoof.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_1", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_2", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_3", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_4", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_5", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_6", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_7", "volume": 0.2 },
{ "name": "cobblemon:stepsound/metal/hoof/large/stepsound_metal_hoof_large_8", "volume": 0.2 }
]
},
"stepsound.metal.hoof.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_1", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_2", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_3", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_4", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_5", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_6", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_7", "volume": 0.1 },
{ "name": "cobblemon:stepsound/metal/hoof/medium/stepsound_metal_hoof_medium_8", "volume": 0.1 }
]
},
"stepsound.grass.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "step/grass1", "volume": 0.05 },
{ "name": "step/grass2", "volume": 0.05 },
{ "name": "step/grass3", "volume": 0.05 },
{ "name": "step/grass4", "volume": 0.05 },
{ "name": "step/grass5", "volume": 0.05 },
{ "name": "step/grass6", "volume": 0.05 }
]
},
"stepsound.glass.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "step/stone1", "volume": 0.05 },
{ "name": "step/stone2", "volume": 0.05 },
{ "name": "step/stone3", "volume": 0.05 },
{ "name": "step/stone4", "volume": 0.05 },
{ "name": "step/stone5", "volume": 0.05 },
{ "name": "step/stone6", "volume": 0.05 }
]
},
"stepsound.dirt.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "step/gravel1", "volume": 0.05 },
{ "name": "step/gravel2", "volume": 0.05 },
{ "name": "step/gravel3", "volume": 0.05 },
{ "name": "step/gravel4", "volume": 0.05 }
]
},
"stepsound.mud.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "block/mud/step1", "volume": 0.2 },
{ "name": "block/mud/step2", "volume": 0.2 },
{ "name": "block/mud/step3", "volume": 0.2 },
{ "name": "block/mud/step4", "volume": 0.2 },
{ "name": "block/mud/step5", "volume": 0.2 },
{ "name": "block/mud/step6", "volume": 0.2 }
]
},
"stepsound.stone.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "step/stone1", "volume": 0.08 },
{ "name": "step/stone2", "volume": 0.08 },
{ "name": "step/stone3", "volume": 0.08 },
{ "name": "step/stone4", "volume": 0.08 },
{ "name": "step/stone5", "volume": 0.08 },
{ "name": "step/stone6", "volume": 0.08 }
]
},
"stepsound.wood.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "block/cherry_wood/step1", "volume": 0.2 },
{ "name": "block/cherry_wood/step2", "volume": 0.2 },
{ "name": "block/cherry_wood/step3", "volume": 0.2 },
{ "name": "block/cherry_wood/step4", "volume": 0.2 },
{ "name": "block/cherry_wood/step5", "volume": 0.2 },
{ "name": "block/cherry_wood/step6", "volume": 0.2 }
]
},
"stepsound.metal.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "block/copper/step1", "volume": 0.2 },
{ "name": "block/copper/step2", "volume": 0.2 },
{ "name": "block/copper/step3", "volume": 0.2 },
{ "name": "block/copper/step4", "volume": 0.2 },
{ "name": "block/copper/step5", "volume": 0.2 },
{ "name": "block/copper/step6", "volume": 0.2 }
]
},
"stepsound.sand.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "step/sand1", "volume": 0.08 },
{ "name": "step/sand2", "volume": 0.08 },
{ "name": "step/sand3", "volume": 0.08 },
{ "name": "step/sand4", "volume": 0.08 },
{ "name": "step/sand5", "volume": 0.08 }
]
},
"stepsound.snow.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "step/snow1", "volume": 0.08 },
{ "name": "step/snow2", "volume": 0.08 },
{ "name": "step/snow3", "volume": 0.08 },
{ "name": "step/snow4", "volume": 0.08 }
]
},
"stepsound.amethyst.minecraft": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "block/amethyst/step1", "volume": 0.11 },
{ "name": "block/amethyst/step2", "volume": 0.11 },
{ "name": "block/amethyst/step3", "volume": 0.11 },
{ "name": "block/amethyst/step4", "volume": 0.11 },
{ "name": "block/amethyst/step5", "volume": 0.11 },
{ "name": "block/amethyst/step6", "volume": 0.11 },
{ "name": "block/amethyst/step7", "volume": 0.11 },
{ "name": "block/amethyst/step8", "volume": 0.11 },
{ "name": "block/amethyst/step9", "volume": 0.11 },
{ "name": "block/amethyst/step10", "volume": 0.11 },
{ "name": "block/amethyst/step11", "volume": 0.11 },
{ "name": "block/amethyst/step12", "volume": 0.11 },
{ "name": "block/amethyst/step13", "volume": 0.11 },
{ "name": "block/amethyst/step14", "volume": 0.11 }
]
},
"stepsound.grass.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_1", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_2", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_3", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_4", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_5", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_6", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_7", "volume": 0.25 },
{ "name": "cobblemon:stepsound/grass/claw/large/stepsound_grass_claw_large_8", "volume": 0.25 }
]
},
"stepsound.grass.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_1", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_2", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_3", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_4", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_5", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_6", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_7", "volume": 0.14 },
{ "name": "cobblemon:stepsound/grass/claw/medium/stepsound_grass_claw_medium_8", "volume": 0.14 }
]
},
"stepsound.dirt.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/dirt/claw/large/stepsound_dirt_claw_large_8", "volume": 0.22 }
]
},
"stepsound.dirt.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/dirt/claw/medium/stepsound_dirt_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.stone.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/stone/claw/large/stepsound_stone_claw_large_8", "volume": 0.22 }
]
},
"stepsound.stone.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/stone/claw/medium/stepsound_stone_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.sand.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/sand/claw/large/stepsound_sand_claw_large_8", "volume": 0.22 }
]
},
"stepsound.sand.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/sand/claw/medium/stepsound_sand_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.snow.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/snow/claw/large/stepsound_snow_claw_large_8", "volume": 0.22 }
]
},
"stepsound.snow.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/snow/claw/medium/stepsound_snow_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.wood.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/wood/claw/large/stepsound_wood_claw_large_8", "volume": 0.22 }
]
},
"stepsound.wood.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/wood/claw/medium/stepsound_wood_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.mud.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/mud/claw/large/stepsound_mud_claw_large_8", "volume": 0.22 }
]
},
"stepsound.mud.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/mud/claw/medium/stepsound_mud_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.glass.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/glass/claw/large/stepsound_glass_claw_large_8", "volume": 0.22 }
]
},
"stepsound.glass.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/glass/claw/medium/stepsound_glass_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.metal.claw.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_1", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_2", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_3", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_4", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_5", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_6", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_7", "volume": 0.22 },
{ "name": "cobblemon:stepsound/metal/claw/large/stepsound_metal_claw_large_8", "volume": 0.22 }
]
},
"stepsound.metal.claw.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_1", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_2", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_3", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_4", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_5", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_6", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_7", "volume": 0.11 },
{ "name": "cobblemon:stepsound/metal/claw/medium/stepsound_metal_claw_medium_8", "volume": 0.11 }
]
},
"stepsound.leaves.universal.large": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_1", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_2", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_3", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_4", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_5", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_6", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_7", "volume": 0.12 },
{ "name": "cobblemon:stepsound/leaves/universal/large/stepsound_leaves_universal_large_8", "volume": 0.12 }
]
},
"stepsound.leaves.universal.medium": {
"subtitle": "subtitles.block.generic.footsteps",
"sounds": [
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_1", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_2", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_3", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_4", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_5", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_6", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_7", "volume": 0.06 },
{ "name": "cobblemon:stepsound/leaves/universal/medium/stepsound_leaves_universal_medium_8", "volume": 0.06 }
]
},
"ride.loop.wind.mono": {
"sounds": [
{
Loaded 100 of 627 files, more files were not shown because too many files have changed in this diff. Show more