Compare commits

..
Author SHA1 Message Date
whatsy 54b52e81de Merge branch 'main' into pc-multiselect 2026-10-30 20:55:21 -04: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
whatsy ec4dea23fc fix flashing 2026-10-26 16:11:26 -04:00
whatsy ed2ad46832 pc health bar display toggle 2026-10-26 15:31:48 -04: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 48ff865c55 cleanup 2026-10-17 19:33:20 -04: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
whatsy e0abdb900d im running out of commit message ideas 2026-10-13 22:58:32 -04:00
whatsy 1b74c2906d Merge remote-tracking branch 'origin/pc-multiselect' into pc-multiselect
# Conflicts:
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pc/PCGUI.kt
#	common/src/main/kotlin/com/cobblemon/mod/common/client/gui/pc/StorageWidget.kt
2026-10-13 20:12:50 -04:00
whatsy d3a616e308 tweaks + bugfixes 2026-10-13 14:14:26 -04:00
whatsy 9a06463871 mass release 2026-10-13 10:56:45 -04:00
whatsy 46b5a046ed highlight selected slots 2026-10-13 10:56:45 -04:00
whatsy ab811ead56 you can now batch move pokemon in the pc 2026-10-13 10:56:45 -04:00
whatsy 09a549ffcd implement draggable selection box in PC GUI 2026-10-13 10:56:45 -04:00
whatsy b393293025 mass release 2026-10-13 10:52:35 -04:00
whatsy 68a760a595 highlight selected slots 2026-10-10 18:18:24 -04:00
whatsy ae7bc6e681 you can now batch move pokemon in the pc 2026-10-09 22:57:48 -04:00
whatsy ed007fe429 implement draggable selection box in PC GUI 2026-10-09 20:51:04 -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
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
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
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
529 changed files with 2628 additions and 1245 deletions

No files matched your search

+29 -2
View File
@@ -1,15 +1,42 @@
# Changelog
## [1.8.x (MONTH xth, 2026)](#1-8-x)
## [1.8.5 (MONTH xth, 2026)](#1-8-5)
### Additions
- You can now drag to select multiple Pokémon inside the PC menu.
### Pokémon Added
#### Generation 4
#### Generation 5
#### Generation 6
#### Generation 7
#### Generation 8
#### Generation 9
### Model updates for the following Pokémon
### Animation updates for the following Pokémon
- Added the `healersSetSpawn` gamerule. When enabled, using a healing machine will set your spawnpoint.
### Changes
- Amethyst now grows from Typegem core blocks like typegems do
### Fixes
- Fixed Ponigiri food values for AppleSkin integration
- Fixed errors emitted by typos in Parched Peat bog structure files
### Developer
### Molang & Datapacks
- `q.is_standing_on_blocks` now properly checks for non-full blocks like slabs, Mud or Soul Sand
- Added Molang functions to play footstep sounds based on the corresponding surface. These are intended to be used by Pokémon animations.
- Pokémon can now have their Minecraft footstep sounds silenced in their poser. This doesn't affect emitting game events.
### Particles
### Localization
- Updated translations for:
- Updated translations for:
@@ -15,6 +15,7 @@ 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.world.gamerules.CobblemonGameRules;
@@ -22,10 +23,14 @@ 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 +38,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 +150,17 @@ 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)));
}
}
}
@@ -42,14 +42,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
@@ -106,7 +101,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
@@ -479,12 +473,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))
@@ -574,6 +566,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(SwapPartyPokemonPacket.ID, SwapPartyPokemonPacket::decode, SwapPartyPokemonHandler))
list.add(PacketRegisterInfo(MovePCPokemonPacket.ID, MovePCPokemonPacket::decode, MovePCPokemonHandler))
list.add(PacketRegisterInfo(MovePCPokemonBatchPacket.ID, MovePCPokemonBatchPacket::decode, MovePCPokemonBatchHandler))
list.add(PacketRegisterInfo(MovePartyPokemonPacket.ID, MovePartyPokemonPacket::decode, MovePartyPokemonHandler))
list.add(PacketRegisterInfo(SwapPCPartyPokemonPacket.ID, SwapPCPartyPokemonPacket::decode, SwapPCPartyPokemonHandler))
@@ -599,7 +592,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))
@@ -666,4 +658,4 @@ object CobblemonNetwork {
fun sendPacketToPlayer(player: ServerPlayer, packet: NetworkPacket<*>) {
Cobblemon.implementation.networkManager.sendPacketToPlayer(player, packet)
}
}
}
@@ -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 {
}
}
}
}
}
@@ -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()
)
}
}
@@ -29,6 +29,7 @@ import com.cobblemon.mod.common.net.messages.client.storage.pc.InitializePCPacke
import com.cobblemon.mod.common.net.messages.client.storage.pc.MoveClientPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.SetPCPokemonPacket
import com.cobblemon.mod.common.net.messages.client.storage.pc.wallpaper.UnlockPCBoxWallpaperPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.asTranslated
@@ -270,6 +271,34 @@ open class PCStore(
}
}
fun moveBatch(moves: List<Triple<UUID, PCPosition, PCPosition>>): Boolean {
if (moves.isEmpty() || moves.size > MovePCPokemonBatchPacket.MAX_MOVES) return false
if (moves.map { it.second }.toSet().size != moves.size ||
moves.map { it.third }.toSet().size != moves.size ||
moves.map { it.first }.toSet().size != moves.size
) return false
if (moves.any { (_, source, target) -> !isValidPosition(source) || !isValidPosition(target) }) return false
val sourcePositions = moves.mapTo(mutableSetOf()) { it.second }
val pokemonMoves = moves.map { (pokemonID, source, target) ->
val pokemon = get(source)?.takeIf { it.uuid == pokemonID } ?: return false
Triple(source, target, pokemon)
}
if (pokemonMoves.any { (_, target, _) -> get(target) != null && target !in sourcePositions }) return false
pokemonMoves.forEach { (source, _, _) -> setAtPosition(source, null) }
pokemonMoves.forEach { (_, target, pokemon) -> setAtPosition(target, pokemon) }
pokemonMoves.forEach { (_, _, pokemon) ->
sendPacketToObservers(RemoveClientPokemonPacket(this, pokemon.uuid))
}
pokemonMoves.forEach { (_, target, pokemon) ->
sendPacketToObservers(SetPCPokemonPacket(uuid, target) { pokemon })
}
pcChangeObservable.emit(Unit)
return true
}
override fun loadPositionFromNBT(nbt: CompoundTag): StoreCoordinates<PCPosition> {
return StoreCoordinates(this, PCPosition(nbt.getShort(DataKeys.STORE_BOX).toInt(), nbt.getByte(DataKeys.STORE_SLOT).toInt()))
}
@@ -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)
}
}
}
@@ -111,6 +111,7 @@ object CobblemonClient {
val battleOverlay: BattleOverlay by lazy { BattleOverlay() }
val pokedexUsageContext: PokedexUsageContext by lazy { PokedexUsageContext() }
val rideControlsOverlay: RideControlsOverlay by lazy { RideControlsOverlay() }
var pcHealthToggle: Boolean = false
fun onLogin() {
clientPlayerData = ClientGeneralPlayerData()
@@ -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)
}
}
}
@@ -29,7 +29,8 @@ class BoxStorageSlot(
val grabbedSlot = parent.grabbedSlot
return if (grabbedSlot == null) {
true
val pokemon = getPokemon()
pokemon == null || !parent.isInMultiSelection(pokemon, position)
} else {
grabbedSlot.getPokemon() != getPokemon()
}
@@ -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
@@ -51,6 +50,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.sounds.SoundEvent
import kotlin.isInitialized
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.FastColor
class PCGUI(
val pc: ClientPC,
@@ -99,6 +99,8 @@ class PCGUI(
private val buttonWallpaperResource = cobblemonResource("textures/gui/pc/pc_button_set_wallpaper.png")
private val buttonInfoArrow = cobblemonResource("textures/gui/pc/info_arrow.png")
private val buttonHealthToggleResource = cobblemonResource("textures/gui/pc/pc_button_toggle_health.png")
private val topSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_top.png")
private val bottomSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_bottom.png")
private val rightSpacerResource = cobblemonResource("textures/gui/pc/pc_spacer_right.png")
@@ -130,6 +132,55 @@ class PCGUI(
var selectPointerOffsetIncrement = false
var displayOptions = false
private var dragStartX: Int? = null
private var dragStartY: Int? = null
private var dragEndX: Int? = null
private var dragEndY: Int? = null
private var selectionDragActive = false
data class SelectionArea(val left: Int, val top: Int, val right: Int, val bottom: Int)
private data class BoxGridBounds(val left: Int, val top: Int, val right: Int, val bottom: Int) {
fun contains(x: Int, y: Int) = x in left..< right && y >= top && y < bottom
}
private fun getBoxGridBounds(): BoxGridBounds {
val left = storageWidget.x
val top = storageWidget.y
return BoxGridBounds(
left,
top,
left + StorageWidget.SCREEN_WIDTH,
top + StorageWidget.SCREEN_HEIGHT
)
}
fun getSelectionArea(): SelectionArea? {
if (!selectionDragActive) return null
val startX = dragStartX ?: return null
val startY = dragStartY ?: return null
val endX = dragEndX ?: return null
val endY = dragEndY ?: return null
val bounds = getBoxGridBounds()
if (!bounds.contains(startX, startY)) return null
val columns = 6
val rows = 5
val slotPitch = StorageSlot.SIZE + StorageWidget.BOX_SLOT_PADDING
val gridLeft = bounds.left + StorageWidget.BOX_SLOT_START_OFFSET_X
val gridTop = bounds.top + StorageWidget.BOX_SLOT_START_OFFSET_Y + if (displayOptions) 5 else 0
val startColumn = ((startX - gridLeft) / slotPitch).coerceIn(0, columns - 1)
val endColumn = (((endX.coerceIn(bounds.left, bounds.right - 1)) - gridLeft) / slotPitch).coerceIn(0, columns - 1)
val startRow = ((startY - gridTop) / slotPitch).coerceIn(0, rows - 1)
val endRow = (((endY.coerceIn(bounds.top, bounds.bottom - 1)) - gridTop) / slotPitch).coerceIn(0, rows - 1)
return SelectionArea(
gridLeft + minOf(startColumn, endColumn) * slotPitch, // left
gridTop + minOf(startRow, endRow) * slotPitch, // top
gridLeft + (maxOf(startColumn, endColumn) * slotPitch) + StorageSlot.SIZE, // right
gridTop + (maxOf(startRow, endRow) * slotPitch) + StorageSlot.SIZE // bottom
)
}
override fun renderBlurredBackground(delta: Float) {}
override fun renderMenuBackground(context: GuiGraphics) {}
@@ -141,10 +192,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 +210,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 +219,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 +249,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) {
@@ -225,7 +271,6 @@ class PCGUI(
storageWidget.setupStorageSlots()
for (button in optionButtons) {
button.visible = displayOptions
button.highlighted = false
}
}.also { it.highlighted = displayOptions }
)
@@ -256,12 +301,28 @@ class PCGUI(
configuration.showParty = isVisible
wallpaperWidget.visible = !isVisible
(it as IconButton).highlighted = !isVisible
if (!unseenWallpapers.isEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
if (unseenWallpapers.isNotEmpty()) MarkPCBoxWallpapersSeenPacket(unseenWallpapers).sendToServer()
}.also {
it.visible = displayOptions
optionButtons.add(it)
})
val healthButton = this.addRenderableWidget(IconButton(
pX = x + 230,
pY = y + 31,
buttonWidth = 20,
buttonHeight = 20,
resource = buttonHealthToggleResource,
label = "toggle_health_bars",
) {
CobblemonClient.pcHealthToggle = !CobblemonClient.pcHealthToggle
(it as IconButton).highlighted = CobblemonClient.pcHealthToggle
}.also {
it.visible = displayOptions
optionButtons.add(it)
})
healthButton.highlighted = CobblemonClient.pcHealthToggle
PokemonSortMode.entries.forEachIndexed { index, sortType ->
val typeName = sortType.name.lowercase()
this.addRenderableWidget(IconButton(
@@ -321,6 +382,20 @@ class PCGUI(
super.init()
}
private fun renderSelectionBox(context: GuiGraphics, area: SelectionArea) {
context.pose().pushPose()
context.pose().translate(0.0, 0.0, 1500.0)
val borderColor = FastColor.ARGB32.color(255, 205, 212, 255)
val bgColor = FastColor.ARGB32.color(75, 133, 183, 230)
val thickness = 1
context.fill(area.left, area.top, area.right, area.bottom, bgColor)
context.fill(area.left, area.top, area.right, area.top + thickness, borderColor)
context.fill(area.left, area.bottom - thickness, area.right, area.bottom, borderColor)
context.fill(area.left, area.top, area.left + thickness, area.bottom, borderColor)
context.fill(area.right - thickness, area.top, area.right, area.bottom, borderColor)
context.pose().popPose()
}
override fun render(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val matrices = context.pose()
renderBackground(context, mouseX, mouseY, delta)
@@ -461,6 +536,20 @@ class PCGUI(
shadow = true
)
// var dexNo = pokemon.species.nationalPokedexNumber.toString()
// while (dexNo.length < 4) {
// dexNo = "0$dexNo"
// }
// drawScaledText(
// context = context,
// font = CobblemonResources.DEFAULT_LARGE,
// text = Component.literal(dexNo).bold(),
// x = x + 38,
// y = y + 1.5,
// shadow = true,
// scale = 0.85f
// )
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
@@ -691,15 +780,13 @@ class PCGUI(
scale = SCALE
)
if (!optionButtons.isEmpty() && displayOptions) {
if (optionButtons.isNotEmpty() && displayOptions) {
for (button in optionButtons) button.showAlt = hasShiftDown()
}
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)
}
getSelectionArea()?.let { renderSelectionBox(context, it) }
// Item Tooltip
if (pokemon != null) {
@@ -721,34 +808,31 @@ 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)
storageWidget.clearPendingStorageSlotClick()
selectionDragActive = false
val clickX = mouseX.toInt()
val clickY = mouseY.toInt()
if (storageWidget.multiSelection == null && button == 0 &&
getBoxGridBounds().contains(clickX, clickY) &&
storageWidget.pastureWidget == null) {
dragStartX = clickX
dragStartY = clickY
} else {
dragStartX = null
dragStartY = null
}
// 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
@@ -767,15 +851,44 @@ class PCGUI(
storageWidget.pastureWidget?.let { pasture ->
if (pasture.pastureScrollList.isHovered) pasture.pastureScrollList.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
if (button == 0 && storageWidget.pastureWidget == null) {
val startX = dragStartX
val startY = dragStartY
if (startX != null && startY != null) {
val offsetX = mouseX - startX
val offsetY = mouseY - startY
if (offsetX * offsetX + offsetY * offsetY >= 25.0) {
selectionDragActive = true
storageWidget.clearPendingStorageSlotClick()
dragEndX = mouseX.toInt()
dragEndY = mouseY.toInt()
}
}
}
return super.mouseDragged(mouseX, mouseY, button, deltaX, deltaY)
}
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
override fun mouseReleased(mouseX: Double, mouseY: Double, button: Int): Boolean {
val handled = super.mouseReleased(mouseX, mouseY, button)
if (button == 0) {
if (selectionDragActive) {
dragEndX = mouseX.toInt()
dragEndY = mouseY.toInt()
storageWidget.updateSelection()
storageWidget.clearPendingStorageSlotClick()
} else {
storageWidget.completePendingStorageSlotClick(mouseX, mouseY)
}
}
dragStartX = null
dragStartY = null
dragEndX = null
dragEndY = null
selectionDragActive = false
return handled
}
override fun keyPressed(keyCode: Int, scanCode: Int, modifiers: Int): Boolean {
val boxNameFocused = this::boxNameWidget.isInitialized && boxNameWidget.isFocused
val filterFocused = this::filterWidget.isInitialized && filterWidget.isFocused
@@ -0,0 +1,37 @@
package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.pokemon.Pokemon
data class PCMultiSelection(
private val grid: MutableList<MutableList<Pokemon?>> = mutableListOf(),
val sourceBox: Int = 0,
val sourceColumn: Int = 0,
val sourceRow: Int = 0
) {
fun getHorizontalSize(): Int = grid.firstOrNull()?.size ?: 0
fun getVerticalSize(): Int = grid.size
constructor(horizontalSize: Int, verticalSize: Int, sourceBox: Int, sourceColumn: Int, sourceRow: Int) : this(
MutableList(verticalSize) { MutableList<Pokemon?>(horizontalSize) { null } },
sourceBox,
sourceColumn,
sourceRow
)
fun getPokemon(x: Int, y: Int): Pokemon? {
return grid[y][x]
}
fun setPokemon(x: Int, y: Int, pokemon: Pokemon?) {
grid[y][x] = pokemon
}
fun pokemonEntries(): List<Triple<Int, Int, Pokemon>> {
return grid.flatMapIndexed { y, row ->
row.mapIndexedNotNull { x, pokemon -> pokemon?.let { Triple(x, y, it) } }
}
}
fun getPokemonCount(): Int = pokemonEntries().size
}
@@ -45,11 +45,13 @@ class ReleaseButton(
vOffset = if (isHovered(mouseX.toDouble(), mouseY.toDouble())) HEIGHT else 0,
textureHeight = HEIGHT * 2
)
val pokemonCount = parent.multiSelection?.getPokemonCount() ?: 0
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pc.release").bold(),
text = if (pokemonCount > 1)
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
x = x + (WIDTH / 2),
y = y + 3.5,
centered = true,
@@ -10,10 +10,11 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
import com.cobblemon.mod.common.client.gui.drawProfilePokemon
import com.cobblemon.mod.common.client.gui.pasture.PasturePCGUIConfiguration
import com.cobblemon.mod.common.client.gui.PokemonGUIAnimationStyle
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.client.render.models.blockbench.FloatingState
import com.cobblemon.mod.common.client.render.renderScaledGuiItemIcon
@@ -22,13 +23,19 @@ import com.cobblemon.mod.common.pokemon.Pokemon
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.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.client.gui.components.Button
import net.minecraft.client.renderer.RenderType
import net.minecraft.client.sounds.SoundManager
import net.minecraft.network.chat.Component
import net.minecraft.util.Mth
import org.joml.Quaternionf
import org.joml.Vector3f
import kotlin.math.abs
import kotlin.math.max
import kotlin.math.roundToInt
open class StorageSlot(
x: Int, y: Int,
@@ -54,9 +61,17 @@ open class StorageSlot(
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
val selectionArea = parent.pcGui.getSelectionArea()
if (selectionArea != null && parent.getSelectedPokemon(selectionArea).contains(this)) {
val highlightColor = 0x40FFFFFF
context.fill(x + 1, y, x + (SIZE - 1), y + SIZE, highlightColor)
context.fill(x, y + 1, x + 1, y + (SIZE - 1), highlightColor)
context.fill(x + SIZE - 1, y + 1, x + SIZE, y + (SIZE - 1), highlightColor)
}
if (shouldRender()) {
renderSlot(context, x, y, delta)
}
}
fun renderSlot(context: GuiGraphics, posX: Int, posY: Int, partialTicks: Float) {
@@ -130,6 +145,23 @@ open class StorageSlot(
matrixStack = matrices
)
}
// Pokemon health
// Taken from vanilla durability rendering
val currentHealth = pokemon.currentHealth
val maxHealth = pokemon.maxHealth.toFloat()
if (currentHealth < maxHealth && CobblemonClient.pcHealthToggle) {
val damageValue = maxHealth - currentHealth
val f = max(0.0f, (maxHealth - (maxHealth - currentHealth)) / maxHealth)
val color = Mth.hsvToRgb(f / 3.0f, 1.0f, 1.0f)
val totalWidth = 12
val width = (totalWidth - damageValue * totalWidth / pokemon.maxHealth.toFloat()).roundToInt();
val k = posX + 1
val l = posY + 6
val bgColor = if (this is PartyStorageSlot) Colors.BLACK else 0xFF3E3E3E.toInt()
context.fill(RenderType.guiOverlay(), k, l, k + totalWidth, l + 1, bgColor)
context.fill(RenderType.guiOverlay(), k, l, k + width, l + 1, color or bgColor)
}
matrices.popPose()
}
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.client.gui.pc
import com.cobblemon.mod.common.CobblemonNetwork
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.gui.blitk
import com.cobblemon.mod.common.api.storage.StorePosition
import com.cobblemon.mod.common.api.storage.party.PartyPosition
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.api.text.bold
@@ -19,7 +18,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
@@ -40,8 +38,9 @@ import net.minecraft.client.gui.components.Button
import net.minecraft.client.gui.screens.Screen
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import java.util.UUID
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket.PCPokemonMove
class StorageWidget(
pX: Int, pY: Int,
@@ -75,15 +74,17 @@ 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
var selectedPosition: StorePosition? = null
var selectedPosition: PartyPosition? = null
var grabbedSlot: GrabbedStorageSlot? = null
var multiSelection: PCMultiSelection? = null
private val multiGrabbedSlots = mutableMapOf<UUID, GrabbedStorageSlot>()
private var pendingStorageSlotClick: Button? = null
private var movePending = false
var box = 0
set(value) {
field =
@@ -117,16 +118,22 @@ class StorageWidget(
subKey = "ui.generic.yes",
onPress = {
if (canDeleteSelected() && displayConfirmRelease) {
val position = selectedPosition ?: return@ReleaseConfirmButton
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
val selection = multiSelection
if (selection != null) {
selection.pokemonEntries().forEach { (column, row, pokemon) ->
val position = PCPosition(
selection.sourceBox,
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
)
CobblemonNetwork.sendToServer(ReleasePCPokemonPacket(pokemon.uuid, position))
}
} else {
val position = selectedPosition ?: return@ReleaseConfirmButton
val pokemon = getSelectedPokemon() ?: return@ReleaseConfirmButton
val packet = when (position) {
is PartyPosition -> ReleasePartyPokemonPacket(pokemon.uuid, position)
is PCPosition -> ReleasePCPokemonPacket(pokemon.uuid, position)
else -> return@ReleaseConfirmButton
CobblemonNetwork.sendToServer(ReleasePartyPokemonPacket(pokemon.uuid, position))
}
CobblemonNetwork.sendToServer(packet)
playSound(CobblemonSounds.PC_RELEASE)
resetSelected()
displayConfirmRelease = false
@@ -153,9 +160,8 @@ class StorageWidget(
}
fun canDeleteSelected(): Boolean {
return !(selectedPosition is PartyPosition && party.filterNotNull().size <= 1)
&& selectedPosition != null
&& grabbedSlot != null
multiSelection?.let { return it.pokemonEntries().isNotEmpty() }
return party.filterNotNull().size > 1 && selectedPosition != null && grabbedSlot != null
}
fun setupStorageSlots() {
@@ -174,7 +180,7 @@ class StorageWidget(
parent = this,
pc = pc,
position = PCPosition(box, index),
onPress = { this.onStorageSlotClicked(it) }
onPress = { pendingStorageSlotClick = it }
).also { widget ->
this.addWidget(widget)
this.boxSlots.add(widget)
@@ -204,7 +210,7 @@ class StorageWidget(
parent = this,
party = party,
position = PartyPosition(partyIndex),
onPress = { this.onStorageSlotClicked(it) }
onPress = { pendingStorageSlotClick = it }
).also { widget ->
this.addWidget(widget)
this.partySlots.add(widget)
@@ -214,19 +220,117 @@ class StorageWidget(
private fun getSelectedPokemon(): Pokemon? {
val selectedPosition = this.selectedPosition ?: return null
return when (selectedPosition) {
is PCPosition -> pc.get(selectedPosition)
is PartyPosition -> party.get(selectedPosition)
else -> null
return party.get(selectedPosition)
}
fun getSelectedPokemon(area: PCGUI.SelectionArea): List<BoxStorageSlot> {
return boxSlots.filter { slot ->
slot.x < area.right && slot.x + StorageSlot.SIZE > area.left &&
slot.y < area.bottom && slot.y + StorageSlot.SIZE > area.top &&
slot.getPokemon()?.let(pcGui.search::passes) == true
}
}
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)
fun clearPendingStorageSlotClick() {
pendingStorageSlotClick = null
}
fun completePendingStorageSlotClick(mouseX: Double, mouseY: Double) {
val button = pendingStorageSlotClick ?: return
pendingStorageSlotClick = null
if (button.isMouseOver(mouseX, mouseY)) {
onStorageSlotClicked(button)
}
}
fun updateSelection() {
val area = pcGui.getSelectionArea()
if (area == null) {
multiSelection = null
return
}
selectedPosition = null
grabbedSlot = null
pcGui.setPreviewPokemon(null)
val selectedSlots = getSelectedPokemon(area)
if (selectedSlots.isEmpty()) {
multiSelection = null
return
}
val minColumn = selectedSlots.minOf { it.position.slot % 6 }
val maxColumn = selectedSlots.maxOf { it.position.slot % 6 }
val minRow = selectedSlots.minOf { it.position.slot / 6 }
val maxRow = selectedSlots.maxOf { it.position.slot / 6 }
val selection = PCMultiSelection(maxColumn - minColumn + 1, maxRow - minRow + 1, box, minColumn, minRow)
selectedSlots.forEach { slot ->
val pokemon = slot.getPokemon() ?: return@forEach
val slotIndex = slot.position.slot
val column = slotIndex % 6
val row = slotIndex / 6
if (column in minColumn..maxColumn && row in minRow..maxRow) {
selection.setPokemon(column - minColumn, row - minRow, pokemon)
}
}
playSound(CobblemonSounds.PC_GRAB)
multiSelection = selection
}
private fun moveMultiSelectionTo(destination: PCPosition): Boolean {
val selection = multiSelection ?: return false
val width = selection.getHorizontalSize()
val height = selection.getVerticalSize()
val destinationColumn = destination.slot % 6
val destinationRow = destination.slot / 6
if (destinationColumn + width > 6 || destinationRow + height > 5) return false
val moves = mutableListOf<PCPokemonMove>()
val selectedSources = mutableSetOf<PCPosition>()
for (row in 0 until height) {
for (column in 0 until width) {
val pokemon = selection.getPokemon(column, row) ?: continue
val source = PCPosition(selection.sourceBox, (selection.sourceRow + row) * 6 + selection.sourceColumn + column)
val target = PCPosition(destination.box, (destinationRow + row) * 6 + destinationColumn + column)
selectedSources.add(source)
moves.add(PCPokemonMove(pokemon.uuid, source, target))
}
}
if (moves.isEmpty()) return false
for (row in 0 until height) {
for (column in 0 until width) {
if (selection.getPokemon(column, row) == null) continue
val target = PCPosition(destination.box, (destinationRow + row) * 6 + destinationColumn + column)
val displacedPokemon = pc.get(target) ?: continue
if (target in selectedSources) continue
val displacedSource = PCPosition(
selection.sourceBox,
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
)
if (pc.get(displacedSource) != null && displacedSource !in selectedSources) return false
moves.add(PCPokemonMove(displacedPokemon.uuid, target, displacedSource))
}
}
if (moves.map { it.newPosition }.toSet().size != moves.size) return false
MovePCPokemonBatchPacket(moves).sendToServer()
return true
}
fun isInMultiSelection(pokemon: Pokemon, position: PCPosition): Boolean {
val selection = multiSelection ?: return false
return selection.sourceBox == position.box && selection.pokemonEntries().any { it.third == pokemon }
}
override fun renderWidget(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
updatePendingMove()
val matrices = context.pose()
// Party Label
if (pcGui.configuration.showParty) {
@@ -249,11 +353,14 @@ class StorageWidget(
shadow = true
)
val pokemonCount = multiSelection?.getPokemonCount() ?: 0
if (canDeleteSelected() && displayConfirmRelease) {
drawScaledText(
context = context,
font = CobblemonResources.DEFAULT_LARGE,
text = lang("ui.pc.release").bold(),
text = if (pokemonCount > 1)
{ lang("ui.pc.release_all").bold() } else { lang("ui.pc.release").bold() },
x = x + 223,
y = y + 119,
centered = true
@@ -317,6 +424,7 @@ class StorageWidget(
slot.render(context, mouseX, mouseY, delta)
val pokemon = slot.getPokemon()
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
&& multiSelection == null
&& pokemon != null && pokemon != pcGui.previewPokemon
) {
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: false)
@@ -333,6 +441,7 @@ class StorageWidget(
slot.render(context, mouseX, mouseY, delta)
val pokemon = slot.getPokemon()
if (grabbedSlot == null && slot.isHovered(mouseX, mouseY)
&& multiSelection == null
&& pokemon != null && pokemon != pcGui.previewPokemon
) {
pcGui.setPreviewPokemon(pokemon, pcGui.isPreviewInParty ?: true)
@@ -344,15 +453,33 @@ class StorageWidget(
pastureWidget?.render(context, mouseX, mouseY, delta)
matrices.pushPose()
matrices.translate(0.0, 0.0, 1000.0)
grabbedSlot?.render(context, mouseX, mouseY, delta)
renderSelectedPokemon(context, mouseX, mouseY, delta)
matrices.popPose()
}
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
private fun renderSelectedPokemon(context: GuiGraphics, mouseX: Int, mouseY: Int, delta: Float) {
if (movePending) return
val selection = multiSelection
if (selection == null) {
grabbedSlot?.render(context, mouseX, mouseY, delta)
return
}
val slotOffset = StorageSlot.SIZE + BOX_SLOT_PADDING
selection.pokemonEntries().forEach { (column, row, pokemon) ->
val grabbed = multiGrabbedSlots.getOrPut(pokemon.uuid) {
GrabbedStorageSlot(0, 0, this, pokemon)
}
grabbed.render(
context,
mouseX + column * slotOffset,
mouseY + row * slotOffset,
delta
)
}
}
override fun mouseClicked(pMouseX: Double, pMouseY: Double, pButton: Int): Boolean {
if (displayConfirmRelease) {
if (releaseYesButton.isHovered(pMouseX, pMouseY)) releaseYesButton.mouseClicked(pMouseX, pMouseY, pButton)
if (releaseNoButton.isHovered(pMouseX, pMouseY)) releaseNoButton.mouseClicked(pMouseX, pMouseY, pButton)
@@ -384,23 +511,55 @@ class StorageWidget(
else -> return
}
val clickedPokemon = when (clickedPosition) {
is PCPosition -> pc.get(clickedPosition)
is PartyPosition -> party.get(clickedPosition)
else -> null
}
multiSelection?.let { selection ->
when (clickedPosition) {
is PCPosition -> {
if (clickedPosition.box == selection.sourceBox &&
clickedPosition.slot == selection.sourceRow * 6 + selection.sourceColumn
) {
resetSelected()
} else if (moveMultiSelectionTo(clickedPosition)) {
playSound(CobblemonSounds.PC_DROP)
movePending = true
}
}
is PartyPosition -> {
val selectedPokemon = selection.pokemonEntries().singleOrNull() ?: return
val (column, row, pokemon) = selectedPokemon
val source = PCPosition(
selection.sourceBox,
(selection.sourceRow + row) * 6 + selection.sourceColumn + column
)
if (pc.get(source) !== pokemon) return
val packet = clickedPokemon?.let {
SwapPCPartyPokemonPacket(it.uuid, clickedPosition, pokemon.uuid, source)
} ?: MovePCPokemonToPartyPacket(pokemon.uuid, source, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
movePending = true
}
else -> Unit
}
return
}
// Reset release confirmation
displayConfirmRelease = false
// Clicking on itself, so unselect position
if (this.selectedPosition != null && this.selectedPosition == clickedPosition) {
if (selectedPosition == clickedPosition) {
if (grabbedSlot != null) playSound(CobblemonSounds.PC_DROP)
resetSelected()
return
}
// Check for selecting a Pokémon
val clickedPokemon = when(button) {
is BoxStorageSlot -> pc.get(clickedPosition as PCPosition)
is PartyStorageSlot -> party.get(clickedPosition as PartyPosition)
else -> null
}
val selectOverride = pcGui.configuration.selectOverride
if (selectOverride != null) {
selectOverride(pcGui, clickedPosition, clickedPokemon)
@@ -433,59 +592,47 @@ class StorageWidget(
}
}
this.selectedPosition = clickedPosition
this.pcGui.setPreviewPokemon(clickedPokemon, pcGui.isPreviewInParty ?: false)
grabbedSlot = GrabbedStorageSlot(
x = button.x,
y = button.y,
parent = this,
pokemon = clickedPokemon
)
playSound(CobblemonSounds.PC_GRAB)
if (clickedPosition is PCPosition) {
val column = clickedPosition.slot % 6
val row = clickedPosition.slot / 6
multiSelection = PCMultiSelection(1, 1, clickedPosition.box, column, row).also {
it.setPokemon(0, 0, clickedPokemon)
}
playSound(CobblemonSounds.PC_GRAB)
return
} else if (clickedPosition is PartyPosition) {
this.selectedPosition = clickedPosition
this.pcGui.setPreviewPokemon(clickedPokemon, pcGui.isPreviewInParty ?: false)
grabbedSlot = GrabbedStorageSlot(
x = button.x,
y = button.y,
parent = this,
pokemon = clickedPokemon
)
playSound(CobblemonSounds.PC_GRAB)
}
}
} else {
// Handle movement within the Box
val selectedPokemon = when(this.selectedPosition) {
is PCPosition -> pc.get(this.selectedPosition as PCPosition)
is PartyPosition -> party.get(this.selectedPosition as PartyPosition)
else -> null
} ?: return
// Box to Box
if (this.selectedPosition is PCPosition && clickedPosition is PCPosition) {
val packet = clickedPokemon?.let { SwapPCPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonPacket(selectedPokemon.uuid, selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
// Box to Party
else if (this.selectedPosition is PCPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(clickedPokemon.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PCPosition) }
?: MovePCPokemonToPartyPacket(selectedPokemon.uuid, this.selectedPosition as PCPosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
}
val sourcePosition = selectedPosition ?: return
val selectedPokemon = party.get(sourcePosition) ?: return
// Party to Box
else if (this.selectedPosition is PartyPosition && clickedPosition is PCPosition) {
if (clickedPosition is PCPosition) {
if (ServerSettings.preventCompletePartyDeposit && this.party.filterNotNull().size == 1 && clickedPokemon == null) {
return
}
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPokemon.uuid, clickedPosition) }
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, this.selectedPosition as PartyPosition, clickedPosition)
val packet = clickedPokemon?.let { SwapPCPartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPokemon.uuid, clickedPosition) }
?: MovePartyPokemonToPCPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
movePending = true
}
// Party to Party
else if (this.selectedPosition is PartyPosition && clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, this.selectedPosition as PartyPosition) }
?: MovePartyPokemonPacket(selectedPokemon.uuid, selectedPosition as PartyPosition, clickedPosition)
else if (clickedPosition is PartyPosition) {
val packet = clickedPokemon?.let { SwapPartyPokemonPacket(it.uuid, clickedPosition, selectedPokemon.uuid, sourcePosition) }
?: MovePartyPokemonPacket(selectedPokemon.uuid, sourcePosition, clickedPosition)
packet.sendToServer()
playSound(CobblemonSounds.PC_DROP)
resetSelected()
movePending = true
}
}
}
@@ -493,6 +640,36 @@ class StorageWidget(
fun resetSelected() {
selectedPosition = null
grabbedSlot = null
multiSelection = null
multiGrabbedSlots.clear()
pcGui.setPreviewPokemon(null)
}
// annoyingly complex solution, but server/client desync causes flashing when moving pokemon. this should fix it
// if you can think of a simpler solution, feel free to implement it
private fun updatePendingMove() {
if (!movePending) return
val selection = multiSelection
if (selection != null) {
val updated = selection.pokemonEntries().any { (column, row, pokemon) ->
val source = PCPosition(selection.sourceBox, (selection.sourceRow + row) * 6 + selection.sourceColumn + column)
pc.get(source)?.uuid != pokemon.uuid
}
if (updated) {
movePending = false
resetSelected()
}
return
}
val source = selectedPosition
val pokemon = grabbedSlot?.getPokemon()
if (source != null && pokemon != null) {
if (party.get(source)?.uuid != pokemon.uuid) {
movePending = false
resetSelected()
}
}
}
}
@@ -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
}
}
}
@@ -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
}
}
}
@@ -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
}
}
@@ -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,31 +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.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)
}
companion object {
val ID = cobblemonResource("update_pasture_range")
fun decode(buffer: RegistryFriendlyByteBuf) = UpdatePastureRangePacket(
buffer.readUUID(), PastureRange.decode(buffer)
)
}
}
@@ -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)
}
}
@@ -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()
)
}
}
@@ -0,0 +1,55 @@
/*
* 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://www.mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.server.storage.pc
import com.cobblemon.mod.common.api.net.NetworkPacket
import com.cobblemon.mod.common.api.storage.pc.PCPosition
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readPCPosition
import com.cobblemon.mod.common.util.writePCPosition
import java.util.UUID
import net.minecraft.network.RegistryFriendlyByteBuf
class MovePCPokemonBatchPacket(val moves: List<PCPokemonMove>) : NetworkPacket<MovePCPokemonBatchPacket> {
override val id = ID
data class PCPokemonMove(
val pokemonID: UUID,
val oldPosition: PCPosition,
val newPosition: PCPosition
)
override fun encode(buffer: RegistryFriendlyByteBuf) {
buffer.writeVarInt(moves.size)
moves.forEach { move ->
buffer.writeUUID(move.pokemonID)
buffer.writePCPosition(move.oldPosition)
buffer.writePCPosition(move.newPosition)
}
}
companion object {
val ID = cobblemonResource("move_pc_pokemon_batch")
const val MAX_MOVES = 60
fun decode(buffer: RegistryFriendlyByteBuf): MovePCPokemonBatchPacket {
val size = buffer.readVarInt()
require(size in 1..MAX_MOVES) { "Invalid batch PC move size: $size" }
return MovePCPokemonBatchPacket(
List(size) {
PCPokemonMove(
pokemonID = buffer.readUUID(),
oldPosition = buffer.readPCPosition(),
newPosition = buffer.readPCPosition()
)
}
)
}
}
}
@@ -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)
}
}
}
@@ -0,0 +1,23 @@
/*
* 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://www.mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.serverhandling.storage.pc
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.net.messages.client.storage.pc.ClosePCPacket
import com.cobblemon.mod.common.net.messages.server.storage.pc.MovePCPokemonBatchPacket
import net.minecraft.server.MinecraftServer
import net.minecraft.server.level.ServerPlayer
object MovePCPokemonBatchHandler : ServerNetworkPacketHandler<MovePCPokemonBatchPacket> {
override fun handle(packet: MovePCPokemonBatchPacket, server: MinecraftServer, player: ServerPlayer) {
val pc = PCLinkManager.getPC(player) ?: return run { ClosePCPacket(null).sendToPlayer(player) }
pc.moveBatch(packet.moves.map { Triple(it.pokemonID, it.oldPosition, it.newPosition) })
}
}
@@ -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()
}
@@ -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,7 @@ 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))
}
@@ -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": {
@@ -1299,6 +1299,7 @@
"cobblemon.ui.pc.filter.tooltip": "Format: pikachu shiny held_item lvl=1...",
"cobblemon.ui.pc.options": "Options",
"cobblemon.ui.pc.release": "Release",
"cobblemon.ui.pc.release_all": "Release All",
"cobblemon.ui.sort.name": "Sort by name",
"cobblemon.ui.sort.level": "Sort by level",
"cobblemon.ui.sort.pokedex_number": "Sort by Pokédex number",
@@ -1307,18 +1308,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",
@@ -2031,6 +2031,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 529 files, more files were not shown because too many files have changed in this diff. Show more