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
622 changed files with 4898 additions and 1376 deletions

No files matched your search

+24 -2
View File
@@ -1,15 +1,37 @@
# Changelog
## [1.8.x (MONTH xth, 2026)](#1-8-x)
## [1.9.0 (MONTH xth, 2026)](#1-9-0)
### Additions
- 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
@@ -42,14 +44,9 @@ import com.cobblemon.mod.common.client.net.npc.OpenNPCEditorHandler
import com.cobblemon.mod.common.client.net.orientation.ClientboundUpdateOrientationHandler
import com.cobblemon.mod.common.client.net.pasture.ClosePastureHandler
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureRangeHandler
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureRangePacket
import com.cobblemon.mod.common.net.messages.server.pasture.AdjustPastureRangePacket
import com.cobblemon.mod.common.net.serverhandling.pasture.AdjustPastureRangeHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonPasturedHandler
import com.cobblemon.mod.common.client.net.pasture.PokemonUnpasturedHandler
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureConflictFlagHandler
import com.cobblemon.mod.common.client.net.pasture.UpdatePastureRecoveryHandler
import com.cobblemon.mod.common.client.net.pokedex.ServerConfirmedRegisterHandler
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundSeatAssignmentPacketHandler
import com.cobblemon.mod.common.client.net.pokemon.update.ClientboundUpdateRidingStateHandler
@@ -81,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
@@ -106,7 +105,6 @@ import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonUnpasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureConflictFlagPacket
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureRecoveryPacket
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
@@ -450,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
@@ -479,12 +478,10 @@ object CobblemonNetwork {
// Pasture
list.add(PacketRegisterInfo(OpenPasturePacket.ID, OpenPasturePacket::decode, OpenPastureHandler))
list.add(PacketRegisterInfo(UpdatePastureRangePacket.ID, UpdatePastureRangePacket::decode, UpdatePastureRangeHandler))
list.add(PacketRegisterInfo(ClosePasturePacket.ID, ClosePasturePacket::decode, ClosePastureHandler))
list.add(PacketRegisterInfo(PokemonPasturedPacket.ID, PokemonPasturedPacket::decode, PokemonPasturedHandler))
list.add(PacketRegisterInfo(PokemonUnpasturedPacket.ID, PokemonUnpasturedPacket::decode, PokemonUnpasturedHandler))
list.add(PacketRegisterInfo(UpdatePastureConflictFlagPacket.ID, UpdatePastureConflictFlagPacket::decode, UpdatePastureConflictFlagHandler))
list.add(PacketRegisterInfo(UpdatePastureRecoveryPacket.ID, UpdatePastureRecoveryPacket::decode, UpdatePastureRecoveryHandler))
// Orientation
list.add(PacketRegisterInfo(ClientboundUpdateOrientationPacket.ID, ClientboundUpdateOrientationPacket::decode, ClientboundUpdateOrientationHandler))
@@ -599,7 +596,6 @@ object CobblemonNetwork {
// Pasture
list.add(PacketRegisterInfo(PasturePokemonPacket.ID, PasturePokemonPacket::decode, PasturePokemonHandler))
list.add(PacketRegisterInfo(AdjustPastureRangePacket.ID, AdjustPastureRangePacket::decode, AdjustPastureRangeHandler))
list.add(PacketRegisterInfo(UnpasturePokemonPacket.ID, UnpasturePokemonPacket::decode, UnpasturePokemonHandler))
list.add(PacketRegisterInfo(UnpastureAllPokemonPacket.ID, UnpastureAllPokemonPacket::decode, UnpastureAllPokemonHandler))
list.add(PacketRegisterInfo(SetPastureConflictPacket.ID, SetPastureConflictPacket::decode, SetPastureConflictHandler))
@@ -649,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))
@@ -656,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))
@@ -666,4 +666,4 @@ object CobblemonNetwork {
fun sendPacketToPlayer(player: ServerPlayer, packet: NetworkPacket<*>) {
Cobblemon.implementation.networkManager.sendPacketToPlayer(player, packet)
}
}
}
@@ -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
@@ -15,13 +15,11 @@ import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WANDER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveFloat
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.datafixers.util.Either
import net.minecraft.core.BlockPos
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -71,14 +69,7 @@ class AirWanderTaskConfig : SingleTaskConfig {
val rotVec = entity.getViewVector(0F)
pathCooldown.setWithExpiry(true, 60L)
val tethering = (entity as? PokemonEntity)?.tethering
val horizontal = horizontalRange.resolveInt(runtime)
val vertical = verticalRange.resolveInt(runtime)
val minUpwards = minUpwardsMovement.resolveInt(runtime)
val minDownwards = minDownwardsMovement.resolveInt(runtime)
val target = (0 until 10).asSequence()
.mapNotNull { HoverRandomPos.getPos(entity, horizontal, vertical, rotVec.x, rotVec.y, 1.5707964f, minUpwards, minDownwards) }
.firstOrNull { tethering?.canRoamTo(BlockPos.containing(it)) != false }
val target = HoverRandomPos.getPos(entity, horizontalRange.resolveInt(runtime), verticalRange.resolveInt(runtime), rotVec.x, rotVec.y, 1.5707964f, minUpwardsMovement.resolveInt(runtime), minDownwardsMovement.resolveInt(runtime))
?: return@Trigger false
walkTarget.set(WalkTarget(target, speedMultiplier.resolveFloat(runtime), 3))
lookTarget.set(BlockPosTracker(target))
@@ -87,4 +78,4 @@ class AirWanderTaskConfig : SingleTaskConfig {
}
}
}
}
}
@@ -17,7 +17,6 @@ import com.cobblemon.mod.common.api.ai.config.task.WanderTaskConfig.Companion.WA
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.ai.CobblemonWalkTarget
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveFloat
@@ -96,7 +95,6 @@ class WaterWanderTaskConfig : SingleTaskConfig {
attempts++
target = BehaviorUtils.getRandomSwimmablePos(entity, horizontalRange.resolveInt(mainThreadRuntime), verticalRange.resolveInt(mainThreadRuntime))
?: continue
if ((entity as? PokemonEntity)?.tethering?.canRoamTo(BlockPos.containing(target)) == false) continue
pos = BlockPos.containing(target).takeIf(wanderControl::isSuitable)
}
@@ -145,4 +143,4 @@ class WaterWanderTaskConfig : SingleTaskConfig {
}
}
}
}
}
@@ -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()
}
}
@@ -34,7 +34,7 @@ object PastureLinkManager {
fun getLinkByPlayer(player: ServerPlayer): PastureLink? {
val link = getLinkByPlayerId(player.uuid)
if (link != null) {
if (player.level().dimension().location() != link.dimension || !link.pos.closerToCenterThan(player.position(), 10.0)) {
if (!player.level().dimensionTypeRegistration().`is`(link.dimension) || !link.pos.closerToCenterThan(player.position(), 10.0)) {
links.remove(player.uuid)
return null
}
@@ -46,8 +46,8 @@ object PastureLinkManager {
@JvmStatic
fun removeAt(world: ServerLevel, pos: BlockPos) {
links.removeIf { (uuid, pastureLink) ->
val shouldRemove = world.dimension().location() == pastureLink.dimension && pastureLink.pos == pos
if (shouldRemove) uuid.getPlayer()?.sendPacket(ClosePasturePacket())
val shouldRemove = world.dimensionTypeRegistration().`is`(pastureLink.dimension) && pastureLink.pos == pos
uuid.getPlayer()?.sendPacket(ClosePasturePacket())
return@removeIf shouldRemove
}
}
@@ -1,82 +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.pasture
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.network.RegistryFriendlyByteBuf
import kotlin.math.abs
import kotlin.math.ceil
import kotlin.math.floor
data class PastureRange(val north: Int, val east: Int, val south: Int, val west: Int) {
fun isValid(maximum: Int): Boolean =
listOf(north, east, south, west).all { it in 0..maximum } &&
north.toLong() + south >= 8 && east.toLong() + west >= 8
operator fun get(direction: Direction): Int = when (direction) {
Direction.NORTH -> north
Direction.EAST -> east
Direction.SOUTH -> south
Direction.WEST -> west
else -> error("Pasture range directions must be horizontal")
}
fun adjusted(direction: Direction, amount: Int): PastureRange = when (direction) {
Direction.NORTH -> copy(north = north + amount)
Direction.EAST -> copy(east = east + amount)
Direction.SOUTH -> copy(south = south + amount)
Direction.WEST -> copy(west = west + amount)
else -> this
}
fun minPos(origin: BlockPos) = origin.offset(-west, 0, -north)
fun maxPos(origin: BlockPos) = origin.offset(east, 0, south)
fun nearbyBlocks(origin: BlockPos, preferred: BlockPos, width: Double): Sequence<BlockPos> = sequence {
val halfWidth = width / 2.0
val minX = ceil(origin.x - west + halfWidth - 0.5).toInt()
val maxX = floor(origin.x + east - halfWidth - 0.5).toInt()
val minZ = ceil(origin.z - north + halfWidth - 0.5).toInt()
val maxZ = floor(origin.z + south - halfWidth - 0.5).toInt()
if (minX > maxX || minZ > maxZ) return@sequence
val centerX = preferred.x.coerceIn(minX, maxX)
val centerZ = preferred.z.coerceIn(minZ, maxZ)
for (distance in 0..16) {
for (dx in -distance..distance) {
val x = centerX + dx
if (x !in minX..maxX) continue
val dz = distance - abs(dx)
val z = centerZ + dz
if (z in minZ..maxZ) yield(BlockPos(x, origin.y, z))
if (dz != 0 && centerZ - dz in minZ..maxZ) yield(BlockPos(x, origin.y, centerZ - dz))
}
}
}
fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(north)
buffer.writeVarInt(east)
buffer.writeVarInt(south)
buffer.writeVarInt(west)
}
companion object {
const val MAX_DISTANCE = 64
fun fromBounds(origin: BlockPos, min: BlockPos, max: BlockPos) = PastureRange(
origin.z - min.z, max.x - origin.x, max.z - origin.z, origin.x - min.x
)
fun decode(buffer: RegistryFriendlyByteBuf) = PastureRange(
buffer.readVarInt(), buffer.readVarInt(), buffer.readVarInt(), buffer.readVarInt()
)
}
}
@@ -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
}
@@ -25,6 +25,7 @@ import com.cobblemon.mod.common.util.rotateShape
import com.mojang.serialization.MapCodec
import net.minecraft.core.BlockPos
import net.minecraft.core.Direction
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.util.StringRepresentable
@@ -282,13 +283,11 @@ class PastureBlock(settings: Properties): BaseEntityBlock(settings), SimpleWater
pastureId = linkId,
permissions = perms,
limit = baseEntity.getMaxTethered(),
tetheredPokemon = baseEntity.tetheredPokemon.mapNotNull { it.toDTO(player) },
range = baseEntity.getRange(),
facing = state.getValue(HorizontalDirectionalBlock.FACING)
tetheredPokemon = baseEntity.tetheredPokemon.mapNotNull { it.toDTO(player) }
)
)
PastureLinkManager.createLink(player.uuid, PastureLink(linkId, pcId, world.dimension().location(), getBasePosition(state, pos), perms))
PastureLinkManager.createLink(player.uuid, PastureLink(linkId, pcId, ResourceLocation.tryParse(world.dimensionTypeRegistration().registeredName)!!, getBasePosition(state, pos), perms))
world.playSoundServer(
position = pos.bottomCenter,
@@ -367,4 +366,4 @@ class PastureBlock(settings: Properties): BaseEntityBlock(settings), SimpleWater
return state
}
}
}
@@ -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)
}
}
@@ -14,19 +14,15 @@ import com.cobblemon.mod.common.CobblemonBlocks
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
import com.cobblemon.mod.common.advancement.CobblemonCriteria
import com.cobblemon.mod.common.api.pasture.PastureLinkManager
import com.cobblemon.mod.common.api.pasture.PastureRange
import com.cobblemon.mod.common.api.scheduling.afterOnServer
import com.cobblemon.mod.common.api.text.italicise
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.block.PastureBlock
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.pasture.ClosePasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureRecoveryPacket
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureConflictFlagPacket
import com.cobblemon.mod.common.net.serverhandling.storage.SendOutPokemonHandler
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
@@ -43,6 +39,7 @@ import net.minecraft.core.Vec3i
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtUtils
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerLevel
import net.minecraft.server.level.ServerPlayer
import net.minecraft.tags.FluidTags
@@ -51,61 +48,38 @@ import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.level.block.entity.BlockEntity
import net.minecraft.world.level.block.entity.BlockEntityTicker
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.entity.EntityTypeTest
import net.minecraft.world.phys.AABB
import net.minecraft.world.phys.Vec3
class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
BlockEntity(CobblemonBlockEntities.PASTURE, pos, state) {
open class Tethering(
var minRoamPos: BlockPos,
var maxRoamPos: BlockPos,
val minRoamPos: BlockPos,
val maxRoamPos: BlockPos,
val playerId: UUID,
val playerName: String,
val tetheringId: UUID,
val pokemonId: UUID,
val pcId: UUID,
var entityId: Int,
val pasturePos: BlockPos,
var entityUuid: UUID? = null,
var lastEntityPos: BlockPos? = null,
var respawnAt: Long = 0L,
var healingAfterFaint: Boolean = false,
var conflictEnabled: Boolean? = null
val entityId: Int,
val pasturePos: BlockPos
) {
fun getPokemon(): Pokemon? {
val srv = server() ?: return null
val pc = Cobblemon.storage.getPC(pcId, srv.registryAccess())
return pc[pokemonId]
}
val box get() = AABB(minRoamPos.toVec3d(), maxRoamPos.toVec3d())
fun canRoamTo(x: Double, z: Double) = x >= minRoamPos.x && x < maxRoamPos.x && z >= minRoamPos.z && z < maxRoamPos.z
open fun canRoamTo(pos: BlockPos) = canRoamTo(pos.x + 0.5, pos.z + 0.5)
internal fun beginRecovery(now: Long, fainted: Boolean) {
if (fainted && !healingAfterFaint) {
healingAfterFaint = true
respawnAt = now + FAINT_COOLDOWN
} else if (respawnAt == 0L) {
respawnAt = now + MISSING_ENTITY_COOLDOWN
}
}
val box = AABB(minRoamPos.toVec3d(), maxRoamPos.toVec3d())
open fun canRoamTo(pos: BlockPos) = box.contains(pos.center)
fun toDTO(player: ServerPlayer): OpenPasturePacket.PasturePokemonDataDTO? {
val pokemon = getPokemon() ?: return null
val entity = pokemon.entity
val behaviourFlags = (entity?.getActiveBehaviourFlags() ?: emptySet()).toMutableSet()
when (conflictEnabled) {
true -> behaviourFlags.add(PokemonBehaviourFlag.PASTURE_CONFLICT)
false -> behaviourFlags.remove(PokemonBehaviourFlag.PASTURE_CONFLICT)
null -> Unit
}
val behaviourFlags = entity?.getActiveBehaviourFlags() ?: emptySet()
return OpenPasturePacket.PasturePokemonDataDTO(
pokemonId = pokemonId,
@@ -117,16 +91,12 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
heldItem = pokemon.heldItem(),
level = pokemon.level,
entityKnown = (entity?.tethering?.tetheringId == tetheringId),
behaviourFlags = behaviourFlags,
healingAfterFaint = healingAfterFaint,
respawnAt = respawnAt
behaviourFlags = behaviourFlags
)
}
}
companion object {
private const val FAINT_COOLDOWN = 24000L
private const val MISSING_ENTITY_COOLDOWN = 200L
internal val TICKER = BlockEntityTicker<PokemonPastureBlockEntity> { world, _, _, blockEntity ->
if (world.isClientSide) return@BlockEntityTicker
blockEntity.ticksUntilCheck--
@@ -164,26 +134,6 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
fun getMaxTethered() = Cobblemon.config.defaultPasturedPokemonLimit
fun getRange() = PastureRange.fromBounds(blockPos, minRoamPos, maxRoamPos)
fun setRange(range: PastureRange) {
minRoamPos = range.minPos(blockPos).atY(minRoamPos.y)
maxRoamPos = range.maxPos(blockPos).atY(maxRoamPos.y)
tetheredPokemon.forEach { tethering ->
tethering.minRoamPos = minRoamPos
tethering.maxRoamPos = maxRoamPos
tethering.getPokemon()?.entity?.let { entity ->
if (entity.tethering?.tetheringId == tethering.tetheringId) {
entity.tethering = tethering
entity.navigation.stop()
entity.brain.eraseMemory(MemoryModuleType.WALK_TARGET)
entity.checkPastureTether()
}
}
}
setChanged()
}
fun canAddPokemon(player: ServerPlayer, pokemon: Pokemon, maxPerPlayer: Int): Boolean {
val forThisPlayer = tetheredPokemon.count { it.playerId == player.uuid }
// Shouldn't be possible, client should've prevented it
@@ -208,64 +158,57 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
fun tether(player: ServerPlayer, pokemon: Pokemon, directionToBehind: Direction): Boolean {
val tethering = Tethering(
minRoamPos, maxRoamPos, player.uuid, player.gameProfile.name, UUID.randomUUID(),
pokemon.uuid, Cobblemon.storage.getPC(player).uuid, -1, blockPos
)
if (!spawnPokemon(pokemon, tethering, directionToBehind)) return false
pokemon.tetheringId = tethering.tetheringId
pokemon.onChange()
tetheredPokemon.add(tethering)
tethering.toDTO(player)?.let { player.sendPacket(PokemonPasturedPacket(it)) }
setChanged()
CobblemonCriteria.PASTURE_USE.trigger(player, pokemon)
return true
}
private fun spawnPokemon(pokemon: Pokemon, tethering: Tethering, directionToBehind: Direction): Boolean {
val world = level ?: return false
val entity = PokemonEntity(world, pokemon = pokemon)
entity.refreshDimensions()
val position = findSpawnPosition(entity, tethering.lastEntityPos, directionToBehind) ?: return false
entity.setPos(position)
entity.beamMode = 2
entity.tethering = tethering
entity.setBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT, tethering.conflictEnabled == true)
if (!world.addFreshEntity(entity)) return false
if (tethering.conflictEnabled == true) entity.remakeBrain()
tethering.entityId = entity.id
tethering.entityUuid = entity.uuid
tethering.lastEntityPos = entity.blockPosition()
tethering.respawnAt = 0L
syncRecovery(tethering)
afterOnServer(seconds = 0.5F) {
if (!entity.isRemoved && entity.pokemon.shiny) {
SpawnSnowstormEntityParticlePacket(cobblemonResource("shiny_ring"), entity.id, listOf("shiny_particles", "middle"))
.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
}
entity.after(seconds = SendOutPokemonHandler.SEND_OUT_DURATION) {
entity.beamMode = 0
}
}
return true
}
val width = entity.boundingBox.xsize
fun findSpawnPosition(entity: PokemonEntity, preferred: BlockPos? = null, direction: Direction = blockState.getValue(HorizontalDirectionalBlock.FACING)): Vec3? {
val world = level ?: return null
val dimensions = entity.getDimensions(Pose.STANDING)
val idealPlace = preferred ?: blockPos.offset(direction.normal.multiply(ceil(dimensions.width / 2.0).toInt() + 1))
for (candidate in getRange().nearbyBlocks(blockPos, idealPlace, dimensions.width.toDouble())) {
if (!world.hasChunkAt(candidate)) continue
val box = dimensions.makeBoundingBox(candidate.center.subtract(0.0, 0.5, 0.0))
val fixedPosition = makeSuitableY(world, candidate, entity, box) ?: continue
val position = fixedPosition.center.subtract(0.0, 0.5, 0.0)
val spawnBox = dimensions.makeBoundingBox(position)
if (world.noCollision(entity, spawnBox) &&
world.getEntitiesOfClass(PokemonEntity::class.java, spawnBox) { it != entity }.isEmpty()) {
return position
val idealPlace = blockPos.offset(directionToBehind.normal.multiply(ceil(width).toInt() + 1))
var box = entity.getDimensions(Pose.STANDING).makeBoundingBox(idealPlace.center.subtract(0.0, 0.5, 0.0))
for (i in 0..5) {
box = box.move(directionToBehind.normal.x.toDouble(), 0.0, directionToBehind.normal.z.toDouble())
val fixedPosition = makeSuitableY(world, idealPlace.offset(directionToBehind.normal.multiply(i + 1)), entity, box)
if (fixedPosition != null) {
entity.setPos(fixedPosition.center.subtract(0.0, 0.5, 0.0))
val pc = Cobblemon.storage.getPC(player)
entity.beamMode = 2
afterOnServer(seconds = 0.5F) {
if(entity.pokemon.shiny) {
SpawnSnowstormEntityParticlePacket(cobblemonResource("shiny_ring"), entity.id, listOf("shiny_particles", "middle"))
.sendToPlayersAround(entity.x, entity.y, entity.z, 64.0, entity.level().dimension())
}
entity.after(seconds = SendOutPokemonHandler.SEND_OUT_DURATION) {
entity.beamMode = 0
}
}
if (world.addFreshEntity(entity)) {
val tethering = Tethering(
minRoamPos = minRoamPos,
maxRoamPos = maxRoamPos,
playerId = player.uuid,
playerName = player.gameProfile.name,
tetheringId = UUID.randomUUID(),
pokemonId = pokemon.uuid,
pcId = pc.uuid,
entityId = entity.id,
pasturePos = blockPos
)
pokemon.tetheringId = tethering.tetheringId
tetheredPokemon.add(tethering)
entity.tethering = tethering
tethering.toDTO(player)?.let { player.sendPacket(PokemonPasturedPacket(it)) }
setChanged()
CobblemonCriteria.PASTURE_USE.trigger(player, pokemon)
return true
} else {
Cobblemon.LOGGER.warn("Couldn't spawn pastured Pokémon for some reason")
}
break
}
}
return null
return false
}
private fun togglePastureOn(on: Boolean) {
@@ -346,7 +289,6 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
}
fun checkPokemon() {
val world = level as? ServerLevel ?: return
val deadLinks = mutableListOf<UUID>()
tetheredPokemon.forEach {
val pokemon = it.getPokemon()
@@ -354,8 +296,6 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
deadLinks.add(it.pokemonId)
} else if (pokemon.tetheringId == null || pokemon.tetheringId != it.tetheringId) {
deadLinks.add(it.pokemonId)
} else {
checkDeployment(world, it, pokemon)
}
}
deadLinks.forEach(::releasePokemon)
@@ -363,85 +303,6 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
setChanged()
}
private fun checkDeployment(world: ServerLevel, tethering: Tethering, pokemon: Pokemon) {
val now = world.gameTime
if (pokemon.isFainted() && !tethering.healingAfterFaint) {
tethering.beginRecovery(now, fainted = true)
syncRecovery(tethering)
}
val entity = (pokemon.entity ?: tethering.entityUuid?.let(world::getEntity) as? PokemonEntity)
?.takeIf { !it.isRemoved && it.tethering?.tetheringId == tethering.tetheringId }
if (tethering.conflictEnabled == null && entity != null) {
tethering.conflictEnabled = entity.getBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT)
}
if (entity != null && pokemon.isFainted()) entity.discard()
if (entity != null && !entity.isRemoved) {
if (tethering.conflictEnabled != null && entity.getBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT) != tethering.conflictEnabled) {
entity.setBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT, tethering.conflictEnabled == true)
entity.remakeBrain()
}
tethering.entityUuid = entity.uuid
tethering.lastEntityPos = entity.blockPosition()
entity.tethering = tethering
if (tethering.respawnAt != 0L || tethering.healingAfterFaint) {
tethering.respawnAt = 0L
tethering.healingAfterFaint = false
syncRecovery(tethering)
}
return
}
// A Pokémon in an unloaded chunk is still deployed. Never create its replacement.
val lastPosition = tethering.lastEntityPos
if (lastPosition != null) {
if (!world.hasChunkAt(lastPosition)) return
}
// The last saved position may predate a chunk crossing. Require the entire range
// to be entity-ticking before deciding that its missing Pokémon has disappeared.
for (chunkX in (minRoamPos.x shr 4)..(maxRoamPos.x shr 4)) {
for (chunkZ in (minRoamPos.z shr 4)..(maxRoamPos.z shr 4)) {
if (!world.isPositionEntityTicking(BlockPos(chunkX shl 4, blockPos.y, chunkZ shl 4))) return
}
}
if (tethering.respawnAt == 0L) {
tethering.beginRecovery(now, fainted = false)
syncRecovery(tethering)
return
}
if (now < tethering.respawnAt) return
if (tethering.healingAfterFaint) {
pokemon.heal()
if (pokemon.isFainted()) return
tethering.healingAfterFaint = false
syncRecovery(tethering)
}
val radius = Cobblemon.config.pastureMaxWanderDistance.toDouble()
val nearby = world.getEntitiesOfClass(PokemonEntity::class.java, AABB.ofSize(blockPos.toVec3d(), radius * 2, world.height.toDouble(), radius * 2)).size
val chunkDiameter = (radius / 16) * 2
if (nearby >= Cobblemon.config.pastureMaxPerChunk * chunkDiameter * chunkDiameter ||
!spawnPokemon(pokemon, tethering, blockState.getValue(HorizontalDirectionalBlock.FACING))) {
tethering.respawnAt = now + MISSING_ENTITY_COOLDOWN
syncRecovery(tethering)
}
}
private fun syncRecovery(tethering: Tethering) {
val world = level as? ServerLevel ?: return
val packet = UpdatePastureRecoveryPacket(tethering.pokemonId, tethering.healingAfterFaint, tethering.respawnAt)
world.server.playerList.players.filter(::isPlayerViewing).forEach { it.sendPacket(packet) }
}
fun setConflict(tethering: Tethering, enabled: Boolean) {
tethering.conflictEnabled = enabled
tethering.getPokemon()?.entity?.takeIf { it.tethering?.tetheringId == tethering.tetheringId }?.let {
it.setBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT, enabled)
it.remakeBrain()
}
setChanged()
val world = level as? ServerLevel ?: return
val packet = UpdatePastureConflictFlagPacket(tethering.pokemonId, enabled)
world.server.playerList.players.filter(::isPlayerViewing).forEach { it.sendPacket(packet) }
}
fun onBroken() {
if (level is ServerLevel) {
tetheredPokemon.toList().forEach { releasePokemon(it.pokemonId) }
@@ -451,10 +312,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
fun releasePokemon(pokemonId: UUID) {
val tethering = tetheredPokemon.find { it.pokemonId == pokemonId } ?: return
tethering.getPokemon()?.let { pokemon ->
pokemon.tetheringId = null
pokemon.onChange()
}
tethering.getPokemon()?.tetheringId = null
tetheredPokemon.remove(tethering)
setChanged()
}
@@ -462,10 +320,7 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
fun releaseAllPokemon(playerId: UUID): List<UUID> {
val unpastured = mutableListOf<UUID>()
tetheredPokemon.filter { it.playerId == playerId }.forEach {
it.getPokemon()?.let { pokemon ->
pokemon.tetheringId = null
pokemon.onChange()
}
it.getPokemon()?.tetheringId = null
tetheredPokemon.remove(it)
unpastured.add(it.pokemonId)
}
@@ -488,7 +343,9 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
private fun isPlayerViewing(player: ServerPlayer): Boolean {
val pastureLink = PastureLinkManager.getLinkByPlayer(player)
return pastureLink != null && pastureLink.pos == blockPos && pastureLink.dimension == player.level().dimension().location()
return pastureLink != null && pastureLink.pos == blockPos && pastureLink.dimension == ResourceLocation.tryParse(
player.level().dimensionTypeRegistration().registeredName // todo (techdaan): confirm this is good
)
}
override fun loadAdditional(nbt: CompoundTag, registryLookup: HolderLookup.Provider) {
@@ -496,9 +353,6 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
val list = nbt.getList(DataKeys.TETHER_POKEMON, CompoundTag.TAG_COMPOUND.toInt())
this.ownerId = if (nbt.hasUUID(DataKeys.TETHER_OWNER_ID)) nbt.getUUID(DataKeys.TETHER_OWNER_ID) else null
this.ownerName = nbt.getString(DataKeys.TETHER_OWNER_NAME).takeIf { it.isNotEmpty() } ?: ""
this.minRoamPos = NbtUtils.readBlockPos(nbt, DataKeys.TETHER_MIN_ROAM_POS).orElse(minRoamPos)
this.maxRoamPos = NbtUtils.readBlockPos(nbt, DataKeys.TETHER_MAX_ROAM_POS).orElse(maxRoamPos)
tetheredPokemon.clear()
for (tetheringNBT in list) {
tetheringNBT as CompoundTag
val tetheringId = tetheringNBT.getUUID(DataKeys.TETHERING_ID)
@@ -511,20 +365,17 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
minRoamPos = minRoamPos,
maxRoamPos = maxRoamPos,
playerId = playerId,
playerName = tetheringNBT.getString("PlayerName").ifEmpty { ownerName },
playerName = ownerName,
tetheringId = tetheringId,
pokemonId = pokemonId,
pcId = pcId,
entityId = entityId,
pasturePos = blockPos,
entityUuid = if (tetheringNBT.hasUUID("EntityUUID")) tetheringNBT.getUUID("EntityUUID") else null,
lastEntityPos = NbtUtils.readBlockPos(tetheringNBT, "LastEntityPos").orElse(null),
respawnAt = tetheringNBT.getLong("RespawnAt"),
healingAfterFaint = tetheringNBT.getBoolean("HealingAfterFaint"),
conflictEnabled = if (tetheringNBT.contains("PastureConflict")) tetheringNBT.getBoolean("PastureConflict") else null
pasturePos = blockPos
)
)
}
this.minRoamPos = NbtUtils.readBlockPos(nbt, DataKeys.TETHER_MIN_ROAM_POS).get()
this.maxRoamPos = NbtUtils.readBlockPos(nbt, DataKeys.TETHER_MAX_ROAM_POS).get()
}
override fun saveAdditional(nbt: CompoundTag, registryLookup: HolderLookup.Provider) {
@@ -537,12 +388,6 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
tetheringNBT.putUUID(DataKeys.POKEMON_UUID, tethering.pokemonId)
tetheringNBT.putUUID(DataKeys.PC_ID, tethering.pcId)
tetheringNBT.putInt(DataKeys.TETHERING_ENTITY_ID, tethering.entityId)
tetheringNBT.putString("PlayerName", tethering.playerName)
tethering.entityUuid?.let { tetheringNBT.putUUID("EntityUUID", it) }
tethering.lastEntityPos?.let { tetheringNBT.put("LastEntityPos", NbtUtils.writeBlockPos(it)) }
tetheringNBT.putLong("RespawnAt", tethering.respawnAt)
tetheringNBT.putBoolean("HealingAfterFaint", tethering.healingAfterFaint)
tethering.conflictEnabled?.let { tetheringNBT.putBoolean("PastureConflict", it) }
list.add(tetheringNBT)
}
nbt.put(DataKeys.TETHER_POKEMON, list)
@@ -551,4 +396,4 @@ class PokemonPastureBlockEntity(pos: BlockPos, state: BlockState) :
ownerId?.let { nbt.putUUID(DataKeys.TETHER_OWNER_ID, it) }
nbt.putString(DataKeys.TETHER_OWNER_NAME, this.ownerName)
}
}
}
@@ -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()
}
}
@@ -11,8 +11,6 @@ package com.cobblemon.mod.common.client.gui.pasture
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.pasture.PasturePermissions
import com.cobblemon.mod.common.api.pasture.PastureRange
import net.minecraft.core.Direction
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.client.gui.pc.PCGUIConfiguration
import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
@@ -23,9 +21,7 @@ class PasturePCGUIConfiguration(
val pastureId: UUID,
val limit: Int,
val pasturedPokemon: SettableObservable<List<OpenPasturePacket.PasturePokemonDataDTO>>,
var permissions: PasturePermissions,
var range: PastureRange,
val facing: Direction
var permissions: PasturePermissions
) : PCGUIConfiguration(
exitFunction = { it.closeNormally(unlink = true) },
showParty = false,
@@ -36,6 +32,4 @@ class PasturePCGUIConfiguration(
}
},
canSelect = { pokemon -> !pokemon.isFainted() && pokemon.tetheringId == null }
) {
var editingRange = false
}
)
@@ -58,7 +58,6 @@ class PasturePokemonScrollList(
private val scrollOverlayResource = cobblemonResource("textures/gui/pasture/pasture_scroll_overlay.png")
private val slotResource = cobblemonResource("textures/gui/pasture/pasture_slot.png")
private val slotOwnerResource = cobblemonResource("textures/gui/pasture/pasture_slot_owner.png")
private val slotFaintedResource = cobblemonResource("textures/gui/pasture/pasture_slot_fainted.png")
}
private var scrolling = false
@@ -195,7 +194,7 @@ class PasturePokemonScrollList(
}
)
override fun getNarration() = if (pokemon.healingAfterFaint) lang("ui.pasture.fainted").append(" ").append(pokemon.displayName) else pokemon.displayName
override fun getNarration() = pokemon.displayName
override fun render(
context: GuiGraphics,
@@ -215,7 +214,7 @@ class PasturePokemonScrollList(
val matrixStack = context.pose()
blitk(
matrixStack = matrixStack,
texture = if (pokemon.healingAfterFaint) slotFaintedResource else if (isOwned()) slotOwnerResource else slotResource,
texture = if (isOwned()) slotOwnerResource else slotResource,
x = x,
y = y,
height = SLOT_HEIGHT,
@@ -235,8 +234,7 @@ class PasturePokemonScrollList(
rotation = Quaternionf().fromEulerXYZDegrees(Vector3f(13F, 35F, 0F)),
state = state,
partialTicks = partialTicks,
scale = 4.5F,
a = if (pokemon.healingAfterFaint) 0.7F else 1F
scale = 4.5F
)
matrixStack.popPose()
@@ -251,21 +249,14 @@ class PasturePokemonScrollList(
)
}
val recovering = pokemon.healingAfterFaint || pokemon.respawnAt > 0L
if (recovering) {
if (!pokemon.healingAfterFaint) {
drawScaledText(context, text = lang("ui.pasture.returning"),
x = x + 18, y = y + 7, scale = SCALE, colour = 0xFFFF8585.toInt(), shadow = true)
}
val remainingTicks = (pokemon.respawnAt - (client.level?.gameTime ?: 0L)).coerceAtLeast(0L)
val seconds = (remainingTicks + 19L) / 20L
val timer = if (remainingTicks == 0L) lang("ui.pasture.ready") else "%d:%02d".format(seconds / 60L, seconds % 60L).text()
drawScaledTextJustifiedRight(context = context, text = timer, x = x + 59, y = y + 17,
shadow = true, scale = SCALE)
} else {
drawScaledTextJustifiedRight(context = context, text = lang("ui.lv.number", pokemon.level),
x = x + 59, y = y + 17, shadow = true, scale = SCALE)
}
drawScaledTextJustifiedRight(
context = context,
text = lang("ui.lv.number", pokemon.level),
x = x + 59,
y = y + 17,
shadow = true,
scale = SCALE
)
drawScaledText(
context = context,
@@ -288,7 +279,7 @@ class PasturePokemonScrollList(
}
if (canUnpasture()) {
if (isOwned() && canDefend() == true) {
if (canDefend() == true) {
conflictButton.setPos(x + 44, y + 3)
conflictButton.render(context, mouseX, mouseY, partialTicks)
}
@@ -300,7 +291,7 @@ class PasturePokemonScrollList(
override fun mouseClicked(mouseX: Double, mouseY: Double, delta: Int): Boolean {
if (canUnpasture()) {
if (isOwned() && canDefend() == true && conflictButton.isHovered(mouseX, mouseY)) {
if (canDefend() == true && conflictButton.isHovered(mouseX, mouseY)) {
conflictButton.onPress()
return true
}
@@ -312,4 +303,4 @@ class PasturePokemonScrollList(
return false
}
}
}
}
@@ -1,225 +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.client.gui.pasture
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.font
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pc.StorageWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
import com.cobblemon.mod.common.net.messages.server.pasture.AdjustPastureRangePacket
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.resources.sounds.SimpleSoundInstance
import net.minecraft.core.Direction
import net.minecraft.network.chat.Component
class PastureRangeWidget(x: Int, y: Int, private val configuration: PasturePCGUIConfiguration) :
SoundlessWidget(x, y, StorageWidget.SCREEN_WIDTH, StorageWidget.SCREEN_HEIGHT, lang("ui.pasture.range")) {
private val rangeResource = cobblemonResource("textures/gui/pasture/pasture_range.png")
private val glowResource = cobblemonResource("textures/gui/pc/wallpaper/basic/glow/wallpaper_basic_05.png")
private val overlayResource = cobblemonResource("textures/gui/pc/pc_screen_overlay.png")
private val infoResource = cobblemonResource("textures/gui/pasture/pasture_range_help.png")
private val infoButtonResource = cobblemonResource("textures/gui/pasture/pasture_button.png")
private val infoButtonSize = 17
private val directions = listOf(configuration.facing, configuration.facing.clockWise, configuration.facing.opposite, configuration.facing.counterClockWise)
private val arrowBounds = listOf(
intArrayOf(75, 17, 103, 46), intArrayOf(116, 64, 152, 96),
intArrayOf(75, 109, 103, 143), intArrayOf(26, 64, 62, 96)
)
// Centers of the numeral shadows in pasture_range.png, relative to its (85, 27) panel origin.
private val numberPositions = listOf(89 to 37, 125 to 77, 89 to 114, 53 to 77)
private var hoveredDirection: Direction? = null
private var showInfo = false
val isInfoOpen get() = showInfo
fun closeInfo() { showInfo = false }
private val infoButtonX get() = x + 5
private val infoButtonY get() = y + 5
private val infoPanelX get() = x + 7
private val infoPanelY get() = y + 40
private val infoPanelWidth = 160
private val infoPanelHeight = 74
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
blitk(
matrixStack = context.pose(), texture = PCBoxWallpaperRepository.defaultWallpaper,
x = x, y = y, width = width, height = height
)
blitk(
matrixStack = context.pose(), texture = rangeResource,
x = x + 3, y = y + 3, width = width - 6, height = height - 6,
uOffset = 88, vOffset = 30, textureWidth = 349, textureHeight = 205
)
blitk(
matrixStack = context.pose(), texture = glowResource,
x = x - 17, y = y - 17, width = 208, height = 189
)
blitk(matrixStack = context.pose(), texture = overlayResource, x = x, y = y, width = width, height = height)
hoveredDirection = if (showInfo) null else directionAt(mouseX.toDouble(), mouseY.toDouble())
directions.indexOf(hoveredDirection).takeIf { it >= 0 }?.let { index ->
renderArrowHighlight(context, index)
}
context.pose().pushPose()
context.pose().translate(-0.5F, 0F, 0F)
directions.forEachIndexed { index, direction ->
val (numberX, numberY) = numberPositions[index]
drawScaledText(context, text = Component.literal(configuration.range[direction].toString()),
x = x + numberX + 1, y = y + numberY - 7, centered = true,
colour = 0xFFFFFFFF.toInt())
}
context.pose().popPose()
if (showInfo) context.fill(x + 3, y + 3, x + width - 3, y + height - 3, 0xAA002730.toInt())
val infoHovered = isOverInfoButton(mouseX.toDouble(), mouseY.toDouble())
// Use the same button face and persistent open-state highlight as the gear.
blitk(matrixStack = context.pose(), texture = infoButtonResource,
x = infoButtonX, y = infoButtonY, width = 13, height = infoButtonSize,
textureWidth = 70, textureHeight = 34)
blitk(matrixStack = context.pose(), texture = infoButtonResource,
x = infoButtonX + 13, y = infoButtonY, width = 4, height = infoButtonSize,
uOffset = 66, textureWidth = 70, textureHeight = 34)
if (showInfo || infoHovered) context.fill(infoButtonX + 2, infoButtonY + 2,
infoButtonX + 16, infoButtonY + 15, 0x88FFFFFF.toInt())
blitk(matrixStack = context.pose(), texture = infoResource,
x = infoButtonX + 4.5F, y = infoButtonY + 3.5F, width = 8, height = 10)
if (showInfo) {
val options = Minecraft.getInstance().options
val boxX = infoPanelX
val boxY = infoPanelY
val boxWidth = infoPanelWidth
val boxHeight = infoPanelHeight
context.fill(boxX + 1, boxY, boxX + boxWidth - 1, boxY + boxHeight, 0x96FFFFFF.toInt())
context.fill(boxX, boxY + 1, boxX + boxWidth, boxY + boxHeight - 1, 0x96FFFFFF.toInt())
blitk(matrixStack = context.pose(), texture = PCBoxWallpaperRepository.defaultWallpaper,
x = boxX + 1, y = boxY + 1, width = boxWidth - 2, height = boxHeight - 2,
textureWidth = StorageWidget.SCREEN_WIDTH, textureHeight = StorageWidget.SCREEN_HEIGHT)
val increaseBinding = if (options.keyAttack.matchesMouse(0)) lang("ui.pasture.range.left_click")
else options.keyAttack.translatedKeyMessage.copy()
val decreaseBinding = if (options.keyUse.matchesMouse(1)) lang("ui.pasture.range.right_click")
else options.keyUse.translatedKeyMessage.copy()
val columns = listOf(
Triple(boxX + 43, lang("ui.pasture.range.increase").bold(), listOf(increaseBinding, lang("ui.pasture.range.scroll_up"))),
Triple(boxX + 117, lang("ui.pasture.range.decrease").bold(), listOf(decreaseBinding, lang("ui.pasture.range.scroll_down")))
)
val detailWidth = columns.flatMap { it.third }.maxOf {
Minecraft.getInstance().font.width(it.copy().font(CobblemonResources.DEFAULT_LARGE))
}
val detailScale = minOf(1F, 72F / detailWidth)
drawScaledText(context, font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pasture.range.help_title").bold(), x = boxX + 80, y = boxY + 8,
centered = true, shadow = true)
context.fill(boxX + 8, boxY + 22, boxX + boxWidth - 8, boxY + 23, 0x96FFFFFF.toInt())
context.fill(boxX + 80, boxY + 27, boxX + 81, boxY + 66, 0x96FFFFFF.toInt())
columns.forEach { (centerX, title, details) ->
drawScaledText(context, font = CobblemonResources.DEFAULT_LARGE,
text = title, x = centerX, y = boxY + 29, centered = true, shadow = true)
details.forEachIndexed { index, line ->
drawScaledText(context, font = CobblemonResources.DEFAULT_LARGE,
text = line, x = centerX, y = boxY + 44 + index * 14,
scale = detailScale, centered = true, shadow = true)
}
}
}
}
private fun renderArrowHighlight(context: GuiGraphics, index: Int) {
fun fill(left: Int, top: Int, right: Int, bottom: Int) {
context.fill(x + left, y + top, x + right, y + bottom, 0x66FFFFFF)
}
// Trace the arrow faces in pasture_range.png; horizontal arrows have longer stems.
// Keep their offset drop shadows visible instead of brightening the surrounding background.
when (index) {
0 -> {
for (row in 0..14) {
val halfWidth = 3 + minOf(row, 11)
fill(89 - halfWidth, 17 + row, 89 + halfWidth, 18 + row)
}
fill(81, 32, 97, 41)
}
1, 3 -> {
for (row in 0..26) {
val tip = 141 + minOf(row, 11, 26 - row)
if (index == 1) fill(137, 64 + row, tip, 65 + row)
else fill(178 - tip, 64 + row, 41, 65 + row)
}
if (index == 1) fill(116, 72, 137, 83)
else fill(41, 72, 62, 83)
}
2 -> {
fill(81, 109, 97, 123)
for (row in 0..14) {
val halfWidth = 14 - maxOf(0, row - 3)
fill(89 - halfWidth, 123 + row, 89 + halfWidth, 124 + row)
}
}
}
}
private fun directionAt(mouseX: Double, mouseY: Double): Direction? {
val index = arrowBounds.indexOfFirst { (left, top, right, bottom) ->
mouseX >= x + left && mouseX < x + right &&
mouseY >= y + top && mouseY < y + bottom
}
return directions.getOrNull(index)
}
private fun adjust(direction: Direction, increase: Boolean): Boolean {
AdjustPastureRangePacket(configuration.pastureId, direction, increase).sendToServer()
return true
}
private fun isOverInfoButton(mouseX: Double, mouseY: Double) =
mouseX >= infoButtonX && mouseX < infoButtonX + infoButtonSize &&
mouseY >= infoButtonY && mouseY < infoButtonY + infoButtonSize
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (pButton == 0 && isOverInfoButton(pMouseX, pMouseY)) {
showInfo = !showInfo
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1F))
return true
}
if (showInfo) {
if (pMouseX < infoPanelX || pMouseX >= infoPanelX + infoPanelWidth ||
pMouseY < infoPanelY || pMouseY >= infoPanelY + infoPanelHeight) closeInfo()
return true
}
val direction = directionAt(pMouseX, pMouseY) ?: return false
val options = Minecraft.getInstance().options
return when {
options.keyAttack.matchesMouse(pButton) -> adjust(direction, true)
options.keyUse.matchesMouse(pButton) -> adjust(direction, false)
else -> false
}
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, scrollX: Double, scrollY: Double): Boolean {
if (showInfo) return true
val direction = directionAt(mouseX, mouseY) ?: return false
if (scrollY == 0.0) return false
return adjust(direction, scrollY > 0)
}
override fun keyPressed(pKeyCode: Int, pScanCode: Int, pModifiers: Int): Boolean {
if (showInfo) return !Minecraft.getInstance().options.keyInventory.matches(pKeyCode, pScanCode)
val direction = hoveredDirection ?: return false
val options = Minecraft.getInstance().options
return when {
options.keyAttack.matches(pKeyCode, pScanCode) -> adjust(direction, true)
options.keyUse.matches(pKeyCode, pScanCode) -> adjust(direction, false)
else -> false
}
}
}
@@ -20,9 +20,7 @@ import com.cobblemon.mod.common.net.messages.server.pasture.UnpastureAllPokemonP
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.client.gui.components.Button
class PastureWidget(
val storageWidget: StorageWidget,
@@ -34,8 +32,6 @@ class PastureWidget(
companion object {
private val baseResource = cobblemonResource("textures/gui/pasture/pasture_panel.png")
private val buttonResource = cobblemonResource("textures/gui/pasture/pasture_button.png")
private val gearResource = cobblemonResource("textures/gui/pasture/pasture_range_gear.png")
}
private val recallButton = RecallButton(
@@ -46,14 +42,6 @@ class PastureWidget(
UnpastureAllPokemonPacket(pasturePCGUIConfiguration.pastureId).sendToServer()
}
private val rangeButton = object : Button(x + 6, y + 174, 27, 17, lang("ui.pasture.range"), {
storageWidget.pcGui.setPastureRangeEditing(!pasturePCGUIConfiguration.editingRange)
}, DEFAULT_NARRATION) {
override fun playDownSound(soundManager: SoundManager) {
storageWidget.pcGui.playSound(CobblemonSounds.PC_CLICK)
}
}
val pastureScrollList = PasturePokemonScrollList(
listX = x + 6,
listY = y + 31,
@@ -82,29 +70,11 @@ class PastureWidget(
pastureScrollList.render(context, mouseX, mouseY, delta)
recallButton.render(context, mouseX, mouseY, delta)
rangeButton.message = lang(if (pasturePCGUIConfiguration.editingRange) "ui.pasture.storage" else "ui.pasture.range")
val buttonX = rangeButton.x
val buttonY = rangeButton.y
val highlighted = pasturePCGUIConfiguration.editingRange || rangeButton.isMouseOver(mouseX.toDouble(), mouseY.toDouble())
blitk(matrixStack = context.pose(), texture = buttonResource, x = buttonX, y = buttonY,
width = 23, height = 17, textureWidth = 70, textureHeight = 34)
blitk(matrixStack = context.pose(), texture = buttonResource, x = buttonX + 23, y = buttonY,
width = 4, height = 17, uOffset = 66, textureWidth = 70, textureHeight = 34)
if (highlighted) context.fill(buttonX + 2, buttonY + 2, buttonX + 26, buttonY + 15, 0x88FFFFFF.toInt())
val gearSize = 11
val gearX = buttonX + (rangeButton.width - gearSize) / 2
val gearY = buttonY + (rangeButton.height - gearSize) / 2
if (highlighted) blitk(matrixStack = context.pose(), texture = gearResource,
x = gearX + 1, y = gearY, width = gearSize, height = gearSize,
red = 0.2F, green = 0.2F, blue = 0.2F)
blitk(matrixStack = context.pose(), texture = gearResource,
x = gearX, y = gearY, width = gearSize, height = gearSize)
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (rangeButton.mouseClicked(pMouseX, pMouseY, pButton)) return true
if (recallButton.isHovered(pMouseX, pMouseY)) recallButton.mouseClicked(pMouseX, pMouseY, pButton)
if (pastureScrollList.isHovered(pMouseX, pMouseY)) pastureScrollList.mouseClicked(pMouseX, pMouseY, pButton)
return super.mouseClicked(pMouseX, pMouseY, pButton)
}
}
}
@@ -16,6 +16,7 @@ import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.resources.sounds.SimpleSoundInstance
@@ -64,6 +65,9 @@ class FilterWidget(
renderCursor(context, input)
if (iconHovered(mouseX, mouseY)) {
context.renderTooltip(Minecraft.getInstance().font, lang("ui.pc.filter.tooltip"), mouseX, mouseY)
}
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
@@ -77,4 +81,4 @@ class FilterWidget(
fun iconHovered(mouseX: Int, mouseY: Int): Boolean = mouseX >= (x - 9) && mouseY >= (y + 3)
&& mouseX <= ((x - 9) + (ICON_SIZE * SCALE)) && mouseY <= ((y + 3) + (ICON_SIZE * SCALE))
}
}
@@ -20,7 +20,6 @@ import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.ExitButton
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
import com.cobblemon.mod.common.client.gui.TypeIcon
import com.cobblemon.mod.common.client.gui.summary.Summary
import com.cobblemon.mod.common.client.gui.summary.Summary.Companion.iconCosmeticItemResource
@@ -141,10 +140,7 @@ class PCGUI(
// Add Exit Button
this.addRenderableWidget(
ExitButton(pX = x + 320, pY = y + 186) {
if (storageWidget.editingPastureRange) {
setPastureRangeEditing(false)
playSound(CobblemonSounds.PC_CLICK)
} else if (::wallpaperWidget.isInitialized && wallpaperWidget.visible) {
if (::wallpaperWidget.isInitialized && wallpaperWidget.visible) {
wallpaperWidget.visible = false
configuration.showParty = true
for (button in optionButtons) button.highlighted = false
@@ -162,7 +158,7 @@ class PCGUI(
pX = x + 220,
pY = y + 16,
forward = true
) { if (!storageWidget.editingPastureRange) storageWidget.box += 1 }
) { storageWidget.box += 1 }
)
// Add Backwards Button
@@ -171,7 +167,7 @@ class PCGUI(
pX = x + 117,
pY = y + 16,
forward = false
) { if (!storageWidget.editingPastureRange) storageWidget.box -= 1 }
) { storageWidget.box -= 1 }
)
// Add Filter Widget
@@ -201,8 +197,6 @@ class PCGUI(
storageWidget = storageWidget
)
this.addRenderableWidget(boxNameWidget)
filterWidget.active = !storageWidget.editingPastureRange
boxNameWidget.active = !storageWidget.editingPastureRange
// Initialise box options if not pasture
if (storageWidget.pastureWidget == null) {
@@ -697,10 +691,6 @@ class PCGUI(
super.render(context, mouseX, mouseY, delta)
if (!storageWidget.editingPastureRange && filterWidget.iconHovered(mouseX, mouseY)) {
context.renderTooltip(Minecraft.getInstance().font, lang("ui.pc.filter.tooltip"), mouseX, mouseY)
}
// Item Tooltip
if (pokemon != null) {
val displayedItem = if (showCosmeticItem) pokemon.cosmeticItem else pokemon.heldItemNoCopy()
@@ -721,34 +711,17 @@ class PCGUI(
}
}
fun setPastureRangeEditing(editing: Boolean) {
val pasture = configuration as? PasturePCGUIConfiguration ?: return
pasture.editingRange = editing
if (!editing) storageWidget.pastureRangeWidget?.closeInfo()
filterWidget.active = !editing
boxNameWidget.active = !editing
focused = null
storageWidget.resetSelected()
}
override fun mouseClicked(mouseX: Double, mouseY: Double, button: Int): Boolean {
val rangeWidget = storageWidget.pastureRangeWidget
if (storageWidget.editingPastureRange && rangeWidget?.isInfoOpen == true &&
children().none { it is ExitButton && it.isHovered(mouseX, mouseY) }) {
return rangeWidget.mouseClicked(mouseX, mouseY, button)
}
// Trigger child component function as they will need to check the entire screen click area
if (::boxNameWidget.isInitialized && !storageWidget.editingPastureRange) boxNameWidget.mouseClicked(mouseX, mouseY, button)
if (::boxNameWidget.isInitialized) boxNameWidget.mouseClicked(mouseX, mouseY, button)
return super.mouseClicked(mouseX, mouseY, button)
}
override fun mouseScrolled(mouseX: Double, mouseY: Double, amount: Double, verticalAmount: Double): Boolean {
if (storageWidget.editingPastureRange &&
storageWidget.pastureRangeWidget?.mouseScrolled(mouseX, mouseY, amount, verticalAmount) == true) return true
storageWidget.pastureWidget?.let { pasture ->
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
}
if (!storageWidget.editingPastureRange && storageWidget.isHovered && mouseX < (storageWidget.x + StorageWidget.SCREEN_WIDTH)) this.storageWidget.box -= verticalAmount.toInt()
if (storageWidget.isHovered && mouseX < (storageWidget.x + StorageWidget.SCREEN_WIDTH)) this.storageWidget.box -= verticalAmount.toInt()
if (::wallpaperWidget.isInitialized && wallpaperWidget.isHovered) wallpaperWidget.mouseScrolled(mouseX, mouseY, amount, verticalAmount)
val infoBoxX = ((width - BASE_WIDTH) / 2) + 9
@@ -771,11 +744,6 @@ class PCGUI(
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
if (storageWidget.editingPastureRange) {
if (keyCode != InputConstants.KEY_ESCAPE &&
storageWidget.pastureRangeWidget?.keyPressed(keyCode, scanCode, modifiers) == true) return true
if (keyCode == InputConstants.KEY_RIGHT || keyCode == InputConstants.KEY_LEFT) return true
}
val boxNameFocused = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
val filterFocused = this::filterWidget.isInitialized && filterWidget.isFocused
@@ -19,7 +19,6 @@ import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
import com.cobblemon.mod.common.client.gui.pasture.PastureWidget
import com.cobblemon.mod.common.client.gui.pasture.PastureRangeWidget
import com.cobblemon.mod.common.client.gui.summary.widgets.SoundlessWidget
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.gui.PCBoxWallpaperRepository
@@ -75,9 +74,6 @@ class StorageWidget(
private val releaseNoButton: ReleaseConfirmButton
var pastureWidget: PastureWidget? = null
val pastureRangeWidget = (pcGui.configuration as? PasturePCGUIConfiguration)?.let { PastureRangeWidget(x, y, it) }
val editingPastureRange: Boolean
get() = (pcGui.configuration as? PasturePCGUIConfiguration)?.editingRange == true
var displayConfirmRelease = false
var screenLoaded = false
@@ -222,11 +218,6 @@ class StorageWidget(
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (editingPastureRange) {
pastureRangeWidget?.render(context, mouseX, mouseY, delta)
pastureWidget?.render(context, mouseX, mouseY, delta)
return
}
val matrices = context.pose()
// Party Label
if (pcGui.configuration.showParty) {
@@ -349,10 +340,6 @@ class StorageWidget(
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (editingPastureRange) {
return pastureRangeWidget?.mouseClicked(pMouseX, pMouseY, pButton) == true ||
pastureWidget?.mouseClicked(pMouseX, pMouseY, pButton) == true
}
if (displayConfirmRelease) {
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
if (releaseNoButton.isHovered(pMouseX, pMouseY)) releaseNoButton.mouseClicked(pMouseX, pMouseY, pButton)
@@ -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)
}
}
@@ -23,8 +23,6 @@ object OpenPastureHandler : ClientNetworkPacketHandler<OpenPasturePacket> {
pastureId = packet.pastureId,
limit = packet.limit,
permissions = packet.permissions,
range = packet.range,
facing = packet.facing,
pasturedPokemon = SettableObservable(packet.tetheredPokemon)
)
@@ -1,26 +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.client.net.pasture
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
import com.cobblemon.mod.common.client.gui.pc.PCGUI
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureRangePacket
import net.minecraft.client.Minecraft
import net.minecraft.client.resources.sounds.SimpleSoundInstance
object UpdatePastureRangeHandler : ClientNetworkPacketHandler<UpdatePastureRangePacket> {
override fun handle(packet: UpdatePastureRangePacket, client: Minecraft) {
val configuration = (client.screen as? PCGUI)?.configuration as? PasturePCGUIConfiguration ?: return
if (configuration.pastureId != packet.pastureId) return
configuration.range = packet.range
client.soundManager.play(SimpleSoundInstance.forUI(CobblemonSounds.PC_CLICK, 1F))
}
}
@@ -1,24 +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.client.net.pasture
import com.cobblemon.mod.common.api.net.ClientNetworkPacketHandler
import com.cobblemon.mod.common.client.gui.pc.PCGUI
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureRecoveryPacket
import net.minecraft.client.Minecraft
object UpdatePastureRecoveryHandler : ClientNetworkPacketHandler<UpdatePastureRecoveryPacket> {
override fun handle(packet: UpdatePastureRecoveryPacket, client: Minecraft) {
val slots = (client.screen as? PCGUI)?.storage?.pastureWidget?.pastureScrollList?.children() ?: return
slots.firstOrNull { it.pokemon.pokemonId == packet.pokemonId }?.pokemon?.apply {
healingAfterFaint = packet.fainted
respawnAt = packet.respawnAt
}
}
}
@@ -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 {
@@ -160,5 +160,4 @@ class CobblemonChestBoatEntity(entityType: EntityType<CobblemonChestBoatEntity>,
private const val INVENTORY_SLOTS = 27
}
}
@@ -166,6 +166,7 @@ import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.LightLayer
import net.minecraft.world.level.block.Blocks
import net.minecraft.world.level.block.HorizontalDirectionalBlock
import net.minecraft.world.level.block.SuspiciousEffectHolder
import net.minecraft.world.level.block.state.BlockState
import net.minecraft.world.level.gameevent.GameEvent
@@ -303,7 +304,6 @@ open class PokemonEntity(
var drops: DropTable? = null
var tethering: PokemonPastureBlockEntity.Tethering? = null
private var nextPastureReturnAttempt = 0
// TODO review if we still want this
var queuedToDespawn = false
@@ -738,29 +738,34 @@ open class PokemonEntity(
// Check to see if the entity needs recalled or repositioned if pastured.
fun checkPastureTether() {
val currentTethering = tethering ?: return
val world = level() as? ServerLevel ?: return
if (ticksLived == 1 && world.hasChunkAt(currentTethering.pasturePos)) {
val pasture = world.getBlockEntity(currentTethering.pasturePos) as? PokemonPastureBlockEntity
pasture?.tetheredPokemon?.find { it.tetheringId == currentTethering.tetheringId }?.let { tethering = it }
}
if (tethering!!.canRoamTo(x, z) || !world.hasChunkAt(currentTethering.pasturePos)) return
navigation.stop()
(moveControl as? PokemonMoveControl)?.apply { stop(); stopBanking() }
brain.eraseMemory(MemoryModuleType.WALK_TARGET)
deltaMovement = Vec3.ZERO
if (ticksLived < nextPastureReturnAttempt) return
val pasture = world.getBlockEntity(currentTethering.pasturePos) as? PokemonPastureBlockEntity
val position = pasture?.findSpawnPosition(this, blockPosition())
if (position != null) {
teleportTo(position.x, position.y, position.z)
nextPastureReturnAttempt = 0
} else if (pasture == null) {
tethering = null
pokemon.recall()
return
} else {
nextPastureReturnAttempt = ticksLived + 20
// Teleport a pastured mon back to the pasture block if they are out of range
if (this.tethering != null && !this.tethering!!.box.contains(this.x, this.y, this.z)) {
// Gather the ideal position (behind the pastureBlock)
val pastureBlockPos = this.tethering!!.pasturePos
val state = this.level().getBlockState(pastureBlockPos)
// Check to ensure we are working with a pastureblock here and it hasn't been broken
if (state.block == CobblemonBlocks.PASTURE) {
val direction = state.getValue(HorizontalDirectionalBlock.FACING)
val width = this.boundingBox.xsize
val tpPos = pastureBlockPos.offset(direction.normal.multiply(ceil(width).toInt() + 1)).center
val heightAllowance = 4.0 // Allow spawning up to x blocks above the pastureBlock y level
// Attempt tp and recall if it fails
if (!this.randomTeleport(tpPos.x, tpPos.y + heightAllowance, tpPos.z, false)) {
// If the teleport was not successful then recall the mon
this.tethering = null
this.pokemon.recall()
} else {
// Stop any now invalid navigation post teleport
this.navigation.stop()
}
} else {
// If the block is destroyed or something else went wrong.
this.tethering = null
this.pokemon.recall()
}
}
}
@@ -1795,7 +1800,7 @@ open class PokemonEntity(
removalObservable.emit(reason)
this.brain.clearMemories()
if (reason.shouldDestroy() && pokemon.tetheringId != null && tethering == null) {
if (reason.shouldDestroy() && pokemon.tetheringId != null) {
pokemon.tetheringId = null
}
if (evolutionEntity != null) {
@@ -2739,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)
}
}
@@ -29,7 +29,6 @@ import net.minecraft.tags.FluidTags
import net.minecraft.util.Mth
import net.minecraft.world.entity.ai.attributes.Attributes
import net.minecraft.world.entity.ai.control.MoveControl
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.level.material.FluidState
import net.minecraft.world.level.pathfinder.PathType
import net.minecraft.world.phys.AABB
@@ -107,14 +106,7 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
if (banking) {
pokemonEntity.yRot += bankRightDegreesPerTick
val forward = bankForwardBlocksPerTick * mediumSpeed
val tethering = pokemonEntity.tethering
if (tethering?.canRoamTo(
pokemonEntity.x - Mth.sin(pokemonEntity.yRot.toRadians()) * forward,
pokemonEntity.z + Mth.cos(pokemonEntity.yRot.toRadians()) * forward
) != false) {
pokemonEntity.travel(Vec3(0.0, bankUpwardsBlocksPerTick.toDouble(), bankForwardBlocksPerTick.toDouble()).scale(mediumSpeed.toDouble()))
}
pokemonEntity.travel(Vec3(0.0, bankUpwardsBlocksPerTick.toDouble(), bankForwardBlocksPerTick.toDouble()).scale(mediumSpeed.toDouble()))
if (bankDurationTicks > 0) {
bankDurationTicks--
}
@@ -288,16 +280,6 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
}
}
}
val tethering = pokemonEntity.tethering
val motion = mob.deltaMovement
if (tethering?.canRoamTo(mob.x, mob.z) == true &&
!tethering.canRoamTo(mob.x + motion.x, mob.z + motion.z)) {
mob.navigation.stop()
pokemonEntity.brain.eraseMemory(MemoryModuleType.WALK_TARGET)
stop()
stopBanking()
mob.deltaMovement = Vec3(0.0, motion.y, 0.0)
}
}
/*
@@ -387,4 +369,4 @@ class PokemonMoveControl(val pokemonEntity: PokemonEntity) : MoveControl(pokemon
strafeForwards = 0.0f
strafeRight = 0.0f
}
}
}
@@ -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) {
@@ -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
}
@@ -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) {
@@ -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
}
}
@@ -6,26 +6,23 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.pasture
package com.cobblemon.mod.common.net.messages.client.bag
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.pasture.PastureRange
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
import java.util.UUID
class UpdatePastureRangePacket(val pastureId: UUID, val range: PastureRange) : NetworkPacket<UpdatePastureRangePacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pastureId)
range.encode(buffer)
}
class UpdateBagMenuDataPacket(val categoryId: Int, val scroll: Int): NetworkPacket<UpdateBagMenuDataPacket> {
companion object {
val ID = cobblemonResource("update_pasture_range")
fun decode(buffer: RegistryFriendlyByteBuf) = UpdatePastureRangePacket(
buffer.readUUID(), PastureRange.decode(buffer)
)
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 })
}
}
@@ -10,7 +10,6 @@ package com.cobblemon.mod.common.net.messages.client.pasture
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.pasture.PasturePermissions
import com.cobblemon.mod.common.api.pasture.PastureRange
import com.cobblemon.mod.common.client.net.pasture.OpenPastureHandler
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.net.IntSize
@@ -20,7 +19,6 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.core.Direction
/**
* Opens a pasture GUI using the provided data.
@@ -30,7 +28,7 @@ import net.minecraft.core.Direction
* @author Hiroku
* @since April 9th, 2023
*/
class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val tetheredPokemon: List<PasturePokemonDataDTO>, val permissions: PasturePermissions, val range: PastureRange, val facing: Direction) : NetworkPacket<OpenPasturePacket> {
class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val tetheredPokemon: List<PasturePokemonDataDTO>, val permissions: PasturePermissions) : NetworkPacket<OpenPasturePacket> {
class PasturePokemonDataDTO(
val pokemonId: UUID,
val playerId: UUID,
@@ -41,9 +39,7 @@ class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val
val heldItem: ItemStack,
val level: Int,
val entityKnown: Boolean,
var behaviourFlags: Set<PokemonBehaviourFlag> = emptySet(),
var healingAfterFaint: Boolean = false,
var respawnAt: Long = 0L
var behaviourFlags: Set<PokemonBehaviourFlag> = emptySet()
) {
companion object {
fun decode(buffer: RegistryFriendlyByteBuf): PasturePokemonDataDTO {
@@ -57,8 +53,6 @@ class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val
val level = buffer.readSizedInt(IntSize.U_SHORT)
val entityKnown = buffer.readBoolean()
val behaviourFlags = buffer.readList { it.readEnumConstant(PokemonBehaviourFlag::class.java) }.toSet()
val healingAfterFaint = buffer.readBoolean()
val respawnAt = buffer.readLong()
return PasturePokemonDataDTO(
pokemonId = pokemonId,
@@ -70,9 +64,7 @@ class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val
heldItem = heldItem,
level = level,
entityKnown = entityKnown,
behaviourFlags = behaviourFlags,
healingAfterFaint = healingAfterFaint,
respawnAt = respawnAt
behaviourFlags = behaviourFlags
)
}
}
@@ -88,8 +80,6 @@ class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val
buffer.writeSizedInt(IntSize.U_SHORT, level)
buffer.writeBoolean(entityKnown)
buffer.writeCollection(behaviourFlags) { _, flag -> buffer.writeEnumConstant(flag) }
buffer.writeBoolean(healingAfterFaint)
buffer.writeLong(respawnAt)
}
}
@@ -105,7 +95,7 @@ class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val
dtos.add(PasturePokemonDataDTO.decode(buffer))
}
val permissions = PasturePermissions.decode(buffer)
return OpenPasturePacket(pcId, pastureId, limit, dtos, permissions, PastureRange.decode(buffer), buffer.readEnum(Direction::class.java))
return OpenPasturePacket(pcId, pastureId, limit, dtos, permissions)
}
}
@@ -120,7 +110,5 @@ class OpenPasturePacket(val pcId: UUID, val pastureId: UUID, val limit: Int, val
tethered.encode(buffer)
}
permissions.encode(buffer)
range.encode(buffer)
buffer.writeEnum(facing)
}
}
@@ -1,29 +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.net.messages.client.pasture
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.UUID
import net.minecraft.network.RegistryFriendlyByteBuf
data class UpdatePastureRecoveryPacket(val pokemonId: UUID, val fainted: Boolean, val respawnAt: Long) : NetworkPacket<UpdatePastureRecoveryPacket> {
override val id = ID
companion object {
val ID = cobblemonResource("update_pasture_recovery")
fun decode(buffer: RegistryFriendlyByteBuf) = UpdatePastureRecoveryPacket(buffer.readUUID(), buffer.readBoolean(), buffer.readLong())
}
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pokemonId)
buffer.writeBoolean(fainted)
buffer.writeLong(respawnAt)
}
}
@@ -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) }
}
}
@@ -1,32 +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.net.messages.server.pasture
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.core.Direction
import net.minecraft.network.RegistryFriendlyByteBuf
import java.util.UUID
class AdjustPastureRangePacket(val pastureId: UUID, val direction: Direction, val increase: Boolean) : NetworkPacket<AdjustPastureRangePacket> {
override val id = ID
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeUUID(pastureId)
buffer.writeEnum(direction)
buffer.writeBoolean(increase)
}
companion object {
val ID = cobblemonResource("adjust_pasture_range")
fun decode(buffer: RegistryFriendlyByteBuf) = AdjustPastureRangePacket(
buffer.readUUID(), buffer.readEnum(Direction::class.java), buffer.readBoolean()
)
}
}
@@ -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)
}
}
}
@@ -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.net.serverhandling.pasture
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.pasture.PastureLinkManager
import com.cobblemon.mod.common.api.pasture.PastureRange
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureRangePacket
import com.cobblemon.mod.common.net.messages.server.pasture.AdjustPastureRangePacket
import net.minecraft.core.Direction
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object AdjustPastureRangeHandler : ServerNetworkPacketHandler<AdjustPastureRangePacket> {
override fun handle(packet: AdjustPastureRangePacket, server: MinecraftServer, player: ServerPlayer) {
val link = PastureLinkManager.getLinkByPlayer(player) ?: return
if (link.linkId != packet.pastureId || packet.direction.axis == Direction.Axis.Y) return
val pasture = player.serverLevel().getBlockEntity(link.pos) as? PokemonPastureBlockEntity ?: return
val range = pasture.getRange().adjusted(packet.direction, if (packet.increase) 1 else -1)
if (!range.isValid(PastureRange.MAX_DISTANCE)) {
UpdatePastureRangePacket(link.linkId, pasture.getRange()).sendToPlayer(player)
return
}
pasture.setRange(range)
for (viewer in player.serverLevel().players()) {
val viewerLink = PastureLinkManager.getLinkByPlayer(viewer) ?: continue
if (viewerLink.pos == link.pos && viewerLink.dimension == link.dimension) {
UpdatePastureRangePacket(viewerLink.linkId, range).sendToPlayer(viewer)
}
}
}
}
@@ -9,18 +9,24 @@
package com.cobblemon.mod.common.net.serverhandling.pasture
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.pasture.PastureLinkManager
import com.cobblemon.mod.common.block.entity.PokemonPastureBlockEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonBehaviourFlag
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.net.messages.server.pasture.SetPastureConflictPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureConflictFlagPacket
object SetPastureConflictHandler : ServerNetworkPacketHandler<SetPastureConflictPacket> {
override fun handle(packet: SetPastureConflictPacket, server: MinecraftServer, player: ServerPlayer) {
val link = PastureLinkManager.getLinkByPlayer(player) ?: return
val pasture = player.level().getBlockEntity(link.pos) as? PokemonPastureBlockEntity ?: return
val tethering = pasture.tetheredPokemon.firstOrNull { it.pokemonId == packet.pokemonId && it.playerId == player.uuid } ?: return
pasture.setConflict(tethering, packet.enabled)
val world = player.level()
val entity = world.getEntitiesOfClass(PokemonEntity::class.java, player.boundingBox.inflate(64.0))
.firstOrNull { it.pokemon.uuid == packet.pokemonId }
?: return
entity.setBehaviourFlag(PokemonBehaviourFlag.PASTURE_CONFLICT, packet.enabled)
entity.remakeBrain()
// Send packet from the server to client GUI to update the toggle button
UpdatePastureConflictFlagPacket(packet.pokemonId, packet.enabled).sendToPlayer(player)
}
}
}
@@ -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)
}
}
@@ -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))
}
@@ -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",
@@ -1307,18 +1316,6 @@
"cobblemon.ui.sort.discovered": "Sort by discovered status",
"cobblemon.ui.pasture": "Pasture",
"cobblemon.ui.pasture.recall_all": "Recall All",
"cobblemon.ui.pasture.range": "Range",
"cobblemon.ui.pasture.storage": "PC",
"cobblemon.ui.pasture.fainted": "Fainted",
"cobblemon.ui.pasture.returning": "Returning",
"cobblemon.ui.pasture.ready": "Waiting",
"cobblemon.ui.pasture.range.left_click": "Left click",
"cobblemon.ui.pasture.range.right_click": "Right click",
"cobblemon.ui.pasture.range.help_title": "Range controls",
"cobblemon.ui.pasture.range.increase": "Increase",
"cobblemon.ui.pasture.range.decrease": "Decrease",
"cobblemon.ui.pasture.range.scroll_up": "Scroll up",
"cobblemon.ui.pasture.range.scroll_down": "Scroll down",
"cobblemon.pasture.too_many_nearby": "There are too many Pokémon nearby.",
"cobblemon.ui.interact.give.item": "Change held item",
"cobblemon.ui.interact.give.cosmetic_item": "Change cosmetic item",
@@ -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 622 files, more files were not shown because too many files have changed in this diff. Show more