Compare commits

...
Author SHA1 Message Date
Gitoido Okaski e94b5f775a chore: changelog skip-ci 2026-11-10 05:11:23 +03:00
Gitoido Okaski 069694c782 feat: auto-add technical aspects for cosmetics and held items 2026-11-10 05:02:56 +03:00
Gitoido Okaski 3ab78e0f0b feat: i18n of cosmetic_item command 2026-11-10 04:58:30 +03:00
Gitoido Okaski acb2a6aeaa feat: added cosmetic_item command counterpart to held_item and respective permission 2026-11-10 04:58:07 +03:00
Gitoido Okaski d5ea6e5494 feat: proof of concept for item components support for held and cosmetic item 2026-11-04 20:01:49 +03:00
Polymeta 99e0a26d7b Merge branch 'compat-improvements' into 'main'
teeny fixes to compat

See merge request cable-mc/cobblemon!2520
2026-11-02 09:17:39 +00:00
dhi_holo 71753df513 Leftovers
Also a slight fix to the order in changelog
2026-11-02 13:30:23 +10:00
dhi_holo 729f080f6e Merge branch 'main' into compat-improvements 2026-11-02 13:26:43 +10:00
dhi_holo 13eb57afb1 category fix
Fixing one recipe category I missed teehee
2026-11-02 13:23:42 +10:00
Jackson Bowes 72b6cb06ff Merge branch 'soon-to-be-implemented-steps' into 'main'
Implementing some steps

See merge request cable-mc/cobblemon!2519
2026-11-01 08:15:06 -06:00
Jackson Bowes 74a57eb972 Merge branch 'sterr/move-sound-implementation' into 'main'
Added sounds to Gigaton Hammer and Pyro Ball

See merge request cable-mc/cobblemon!2518
2026-11-01 08:14:14 -06:00
Sterrezo 7246c7f8e6 Update CHANGELOG.md 2026-11-01 13:22:32 +01:00
Sterrezo cd05f741ae Merge branch 'main' into soon-to-be-implemented-steps 2026-11-01 13:21:23 +01:00
Sterrezo 00222d929d Added sounds to Gigaton Hammer and Pyro Ball 2026-11-01 13:18:22 +01:00
Polymeta b8f4637cb3 Merge branch 'feat/power-ride-training' into 'main'
power bracer ride stat training

See merge request cable-mc/cobblemon!2503
2026-10-31 12:33:22 +00:00
Ronak Chavva 9f572250f5 fix(*): modularization 2026-10-31 06:59:37 -05:00
Ronak Chavva 0927d6fa43 fix(*): enum change, configurable item stat 2026-10-31 06:26:45 -05:00
Ronak Chavva a293b3d71f changelog additions 2026-10-31 05:09:36 -05:00
Ronak Chavva 162b1cc68f Merge branch 'main' of https://gitlab.com/ronakch8/cobblemon into feat/power-ride-training 2026-10-31 05:07:29 -05:00
Ronak Chavva 066a6b56f7 Merge branch 'main' of https://gitlab.com/ronakch8/cobblemon into feat/power-ride-training 2026-10-23 18:13:32 -05:00
Ronak Chavva 7687833add mapping rewrite 2026-10-22 21:35:48 -05:00
Ronak Chavva cb9ba82fcc holding power bracer while riding, 1 to all 5 stats 2026-10-22 03:11:14 -05:00
Sterrezo 4221993f7d Added stepsounds to Mr. Mime 2026-10-03 14:39:21 +02:00
Sterrezo a616c25c8c Added stepsounds to Parasect 2026-09-30 14:45:00 +02:00
Sterrezo 6ce4bf5673 Added stepsounds to Rhyhorn 2026-09-30 14:34:07 +02:00
Sterrezo 1b652ba99f Added stepsounds to Rhydron 2026-09-30 14:01:50 +02:00
Sterrezo 11b59df386 Added stepsounds to Dewgong 2026-09-28 20:20:16 +02:00
Sterrezo ac2b068bf3 Added stepsounds to Blastoise 2026-09-28 14:06:42 +02:00
Sterrezo 8e8f346e52 Added stepsounds keyframes to Venusaur 2026-09-28 13:47:47 +02:00
45 changed files with 1366 additions and 760 deletions

No files matched your search

+10
View File
@@ -3,9 +3,17 @@
### Additions
- Added the `healersSetSpawn` gamerule. When enabled, using a healing machine will set your spawnpoint.
- A Pokémon holding a Power item now gains 1 point in the corresponding riding stat every two minutes while being ridden. The interval is configurable, and can be disabled with `-1`.
- Added sounds to Gigaton Hammer and Pyro Ball
- Added stepsounds to Venusaur, Blastoise, Dewgong, Rhydon, Rhyhorn, Parasect and Mr. Mime.
- Added `cosmetic_item` command
### Changes
- Amethyst now grows from Typegem core blocks like typegems do
- Updated the recipe category for the Create compatibility EXP Candy recipe
- Added No Man's Lands' Honeyed Apple to the Leaves Leftovers tag
- Added lore support to held and cosmetic items
- Pokemon can now wear every item with `cobblemon:aspect` component in their cosmetic slot
### Fixes
- Fixed Ponigiri food values for AppleSkin integration
@@ -17,6 +25,8 @@
- `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.
- Cosmetic items can now add aspects automatically to pokemon when wore. for example `cosmetic_minecraft-dirt-test` if pokemon wears a dirt block with `[cobblemon:aspect='test']` component
- Held items can now add automatically aspects to pokemon when held. for example `held_minecraft-dirt` if pokemon holds a dirt block or `cosmetic_minecraft-dirt-test` if pokemon holds a dirt block with `[cobblemon:aspect='test']` component
### Particles
@@ -98,6 +98,7 @@ import com.cobblemon.mod.common.pokemon.aspects.ALPHA_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.CHARACTERISTIC_RAINBOW_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.COSMETIC_SLOT_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.GENDER_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.HELD_ITEM_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.MARK_ASPECT
import com.cobblemon.mod.common.pokemon.aspects.SHINY_ASPECT
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
@@ -326,6 +327,7 @@ object Cobblemon {
SHINY_ASPECT.register()
GENDER_ASPECT.register()
HELD_ITEM_ASPECT.register()
COSMETIC_SLOT_ASPECT.register()
CHARACTERISTIC_RAINBOW_ASPECT.register()
ALPHA_ASPECT.register()
@@ -39,6 +39,7 @@ object CobblemonCommands {
FriendshipCommand.register(dispatcher)
GiveAllPokemon.register(dispatcher)
HeldItemCommand.register(dispatcher, registry)
CosmeticItemCommand.register(dispatcher, registry)
PcCommand.register(dispatcher)
SpawnPokemonFromPool.register(dispatcher)
PokeboxCommand.register(dispatcher)
@@ -77,15 +77,15 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
@JvmField
val INGREDIENT: DataComponentType<IngredientComponent> = create("ingredient", DataComponentType.builder<IngredientComponent>()
.persistent(IngredientComponent.CODEC)
.networkSynchronized(IngredientComponent.PACKET_CODEC)
.build())
.persistent(IngredientComponent.CODEC)
.networkSynchronized(IngredientComponent.PACKET_CODEC)
.build())
@JvmField
val FOOD: DataComponentType<FoodComponent> = create("food", DataComponentType.builder<FoodComponent>()
.persistent(FoodComponent.CODEC)
.networkSynchronized(FoodComponent.PACKET_CODEC)
.build())
.persistent(FoodComponent.CODEC)
.networkSynchronized(FoodComponent.PACKET_CODEC)
.build())
@JvmField
val MOB_EFFECTS: DataComponentType<MobEffectsComponent> = create("mob_effects", DataComponentType.builder<MobEffectsComponent>()
@@ -95,9 +95,15 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
@JvmField
val CRAFTED: DataComponentType<Boolean> = create("crafted", DataComponentType.builder<Boolean>()
.persistent(Codec.BOOL)
.networkSynchronized(ByteBufCodecs.BOOL)
.build())
.persistent(Codec.BOOL)
.networkSynchronized(ByteBufCodecs.BOOL)
.build())
@JvmField
val ASPECT: DataComponentType<BedrockAspectComponent> = create("aspect", DataComponentType.builder<BedrockAspectComponent>()
.persistent(BedrockAspectComponent.CODEC)
.networkSynchronized(BedrockAspectComponent.PACKET_CODEC)
.build())
fun register() {
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
@@ -112,9 +118,9 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:food"), FOOD)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:mob_effects"), MOB_EFFECTS)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:held_item_effect"), HELD_ITEM_EFFECT)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:aspect"), ASPECT)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
override val resourceKey = Registries.DATA_COMPONENT_TYPE
}
}
@@ -37,7 +37,6 @@ object CobblemonPermissions {
@JvmStatic
val HEAL_POKEMON_OTHER = this.create("$HEAL_POKEMON_BASE.other", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
private const val LEVEL_UP_BASE = "${COMMAND_PREFIX}levelup"
@JvmStatic
val LEVEL_UP_SELF = this.create("$LEVEL_UP_BASE.self", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -68,27 +67,24 @@ object CobblemonPermissions {
@JvmStatic
val SPAWN_NPC = this.create("${COMMAND_PREFIX}spawnnpc", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val STOP_BATTLE = this.create("${COMMAND_PREFIX}stopbattle", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val TAKE_POKEMON = this.create("${COMMAND_PREFIX}takepokemon", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val TEACH = this.create("${COMMAND_PREFIX}teach.base", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val TEACH_BYPASS_LEARNSET = this.create("${COMMAND_PREFIX}teach.bypass", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val FRIENDSHIP = this.create("${COMMAND_PREFIX}friendship", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val HELD_ITEM = this.create("${COMMAND_PREFIX}helditem", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val COSMETIC_ITEM = this.create("${COMMAND_PREFIX}cosmeticitem", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@JvmStatic
val CHANGE_MARK = this.create("${COMMAND_PREFIX}changemark", PermissionLevel.CHEAT_COMMANDS_AND_COMMAND_BLOCKS)
@@ -161,4 +157,4 @@ object CobblemonPermissions {
return permission
}
}
}
@@ -26,6 +26,7 @@ import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.locale.Language
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Items
fun describePokemonProperties(properties: PokemonProperties): MutableComponent? {
@@ -57,7 +58,7 @@ fun describePokemonProperties(properties: PokemonProperties): MutableComponent?
}
val heldItemDescriptor = properties.heldItem?.let { heldItem ->
val itemId = heldItem.substringBefore('[').trim().asIdentifierDefaultingNamespace()
val itemId = ResourceLocation.parse("minecraft:dirt")
val item = BuiltInRegistries.ITEM.get(itemId)
if (item == Items.AIR) null else lang("ui.pokedex.requirement.properties.held_item", item.description.plainCopy().green())
}
@@ -139,4 +140,4 @@ private fun describeStringSpeciesFeature(property: StringSpeciesFeature): Mutabl
?: Component.literal(property.value)
return lang(key, value.green())
}
}
@@ -36,6 +36,7 @@ import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.pokemon.stat.CobblemonStatProvider
import com.cobblemon.mod.common.pokemon.status.PersistentStatus
import com.cobblemon.mod.common.util.DataKeys
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.isDouble
import com.cobblemon.mod.common.util.isFloat
@@ -52,8 +53,11 @@ import java.util.UUID
import kotlin.math.min
import kotlin.random.Random
import net.minecraft.ResourceLocationException
import net.minecraft.advancements.critereon.ItemPredicate
import net.minecraft.commands.arguments.item.ItemInput
import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.core.HolderLookup
import net.minecraft.core.component.DataComponentPredicate
import net.minecraft.nbt.CompoundTag
import net.minecraft.nbt.ListTag
import net.minecraft.nbt.NbtOps
@@ -157,7 +161,8 @@ open class PokemonProperties {
null
}
}
props.heldItem = parseString(keyPairs, listOf("helditem", "held_item"))
props.heldItem = parseItem(keyPairs, listOf("helditem", "held_item"))
props.cosmeticItem = parseItem(keyPairs, listOf("cosmetic", "cosmeticitem", "cosmetic_item"))
props.minPerfectIVs = parseIntProperty(keyPairs, listOf("min_perfect_ivs"))?.coerceIn(0, Stats.PERMANENT.size)
props.scaleModifier = parseFloatProperty(keyPairs, listOf("scale_modifier"))
@@ -340,6 +345,20 @@ open class PokemonProperties {
}
return null
}
private fun parseItem(keyPairs: MutableList<Pair<String, String?>>, labels: Iterable<String>): ItemStack? {
val matched = getMatchedKeyPair(keyPairs, labels) ?: return null
val value = matched.second?.lowercase() ?: return null
return try {
val parseResult = ItemParser(RegistryAccessResolver.getAccess())
.parse(StringReader(value))
val input = ItemInput(parseResult.item(), parseResult.components())
input.createItemStack(1, false)
} catch (_: Exception) {
null
}
}
}
var originalString: String = ""
@@ -366,8 +385,8 @@ open class PokemonProperties {
var originalTrainer: String? = null // Original Trainer by Username or UUID
var moves: List<String>? = null
var movesetBuilders: List<ResourceLocation>? = null
var heldItem: String? = null
var cosmeticItem: String? = null
var heldItem: ItemStack? = null
var cosmeticItem: ItemStack? = null
var scaleModifier: Float? = null
var isAlpha: Boolean? = null
@@ -494,16 +513,14 @@ open class PokemonProperties {
pokemon.initializeMovesetFrom(movesetBuilder)
}
}
heldItem?.let { itemKey ->
val server = server() ?: return@let
val parser = ItemParser(server.registryAccess())
val result = parser.parse(StringReader(itemKey))
val stack = ItemStack(result.item)
stack.applyComponents(result.components)
heldItem?.let { stack ->
if (stack.isEmpty) return@let
pokemon.swapHeldItem(stack, decrement = true, aiCanDrop = false)
}
cosmeticItem?.let { stack ->
if (stack.isEmpty) return@let
pokemon.swapCosmeticItem(stack, decrement = true)
}
scaleModifier?.let { pokemon.scaleModifier = it }
pokemon.updateAspects()
}
@@ -564,21 +581,21 @@ open class PokemonProperties {
originalTrainer?.takeIf { it != pokemon.originalTrainer }?.let { return false }
originalTrainerType?.takeIf { it != pokemon.originalTrainerType }?.let { return false }
moves?.takeIf { it.any { move -> pokemon.moveSet.none { it.template.name == move } } }?.let { return false }
heldItem?.takeIf { itemKey ->
val server = server() ?: return@takeIf true
val parser = ItemParser(server.registryAccess())
val result = parser.parse(StringReader(itemKey))
heldItem?.takeIf { stack ->
val predicate = ItemPredicate.Builder.item()
.of(pokemon.heldItem.item)
.hasComponents(DataComponentPredicate.allOf(pokemon.heldItem.components))
.build()
if (!pokemon.heldItem.`is`(result.item)) return@takeIf true
return@takeIf !predicate.test(stack)
}?.let { return false }
cosmeticItem?.takeIf { stack ->
val predicate = ItemPredicate.Builder.item()
.of(pokemon.heldItem.item)
.hasComponents(DataComponentPredicate.allOf(pokemon.heldItem.components))
.build()
for (entry in result.components.entrySet()) {
val targetPropValue = pokemon.heldItem.get(entry.key)
if (targetPropValue != entry.value.get()) {
return@takeIf true
}
}
return@takeIf false
return@takeIf !predicate.test(stack)
}?.let { return false }
scaleModifier?.takeIf { it != pokemon.scaleModifier }?.let { return false }
return true
@@ -706,7 +723,14 @@ open class PokemonProperties {
originalTrainer?.let { nbt.putString(DataKeys.POKEMON_ORIGINAL_TRAINER, it) }
moves?.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_MOVES, it.joinToString(separator = ",")) }
movesetBuilders?.let { nbt.putString(DataKeys.POKEMON_PROPERTIES_MOVESET_BUILDERS, it.joinToString(separator = ",") { source -> source.toString() }) }
heldItem?.let {nbt.putString(DataKeys.POKEMON_PROPERTIES_HELDITEM, it)}
heldItem?.let {
val tag = ItemStack.SINGLE_ITEM_CODEC.encodeStart(NbtOps.INSTANCE, it)
nbt.put(DataKeys.POKEMON_PROPERTIES_HELDITEM, tag.orThrow)
}
cosmeticItem?.let {
val tag = ItemStack.SINGLE_ITEM_CODEC.encodeStart(NbtOps.INSTANCE, it)
nbt.put(DataKeys.POKEMON_PROPERTIES_COSMETICITEM, tag.orThrow)
}
scaleModifier?.let { nbt.putFloat(DataKeys.POKEMON_SCALE_MODIFIER, it) }
val custom = ListTag()
customProperties.map { StringTag.valueOf(it.asString()) }.forEach { custom.add(it) }
@@ -742,8 +766,16 @@ open class PokemonProperties {
originalTrainer = if (tag.contains(DataKeys.POKEMON_ORIGINAL_TRAINER)) tag.getString(DataKeys.POKEMON_ORIGINAL_TRAINER) else null
moves = if (tag.contains(DataKeys.POKEMON_PROPERTIES_MOVES)) tag.getString(DataKeys.POKEMON_PROPERTIES_MOVES).split(",") else null
movesetBuilders = if (tag.contains(DataKeys.POKEMON_PROPERTIES_MOVESET_BUILDERS)) tag.getString(DataKeys.POKEMON_PROPERTIES_MOVESET_BUILDERS).split(",").map { it.asIdentifierDefaultingNamespace() } else null
heldItem = if (tag.contains(DataKeys.POKEMON_PROPERTIES_HELDITEM)) tag.getString(DataKeys.POKEMON_PROPERTIES_HELDITEM) else null
scaleModifier = if (tag.contains(DataKeys.POKEMON_SCALE_MODIFIER)) tag.getFloat(DataKeys.POKEMON_SCALE_MODIFIER) else null
heldItem = when(tag.contains(DataKeys.POKEMON_PROPERTIES_HELDITEM)) {
true -> ItemStack.parse(RegistryAccessResolver.getAccess(), tag.get(DataKeys.POKEMON_PROPERTIES_HELDITEM)!!).getOrNull()
false -> null
}
cosmeticItem = when(tag.contains(DataKeys.POKEMON_PROPERTIES_COSMETICITEM)) {
true -> ItemStack.parse(RegistryAccessResolver.getAccess(), tag.get(DataKeys.POKEMON_PROPERTIES_COSMETICITEM)!!).getOrNull()
false -> null
}
val custom = tag.getList(DataKeys.POKEMON_PROPERTIES_CUSTOM, Tag.TAG_STRING.toInt())
// This is kinda gross
custom.forEach { customProperties.addAll(parse(it.asString).customProperties) }
@@ -780,7 +812,18 @@ open class PokemonProperties {
originalTrainer?.let { json.addProperty(DataKeys.POKEMON_ORIGINAL_TRAINER, it) }
moves?.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_MOVES, it.joinToString(separator = ",")) }
movesetBuilders?.let { json.addProperty(DataKeys.POKEMON_PROPERTIES_MOVESET_BUILDERS, it.joinToString(separator = ",") { source -> source.toString() }) }
heldItem?.let {json.addProperty(DataKeys.POKEMON_PROPERTIES_HELDITEM, it)}
heldItem?.let {
json.add(
DataKeys.POKEMON_PROPERTIES_HELDITEM,
ItemStack.SINGLE_ITEM_CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()
)
}
cosmeticItem?.let {
json.add(
DataKeys.POKEMON_PROPERTIES_COSMETICITEM,
ItemStack.SINGLE_ITEM_CODEC.encodeStart(JsonOps.INSTANCE, it).result().get()
)
}
scaleModifier?.let { json.addProperty(DataKeys.POKEMON_SCALE_MODIFIER, it) }
val custom = JsonArray()
customProperties.map { it.asString() }.forEach { custom.add(it) }
@@ -817,7 +860,12 @@ open class PokemonProperties {
originalTrainer = json.get(DataKeys.POKEMON_ORIGINAL_TRAINER)?.asString
moves = json.get(DataKeys.POKEMON_PROPERTIES_MOVES)?.asString?.split(",")
movesetBuilders = json.get(DataKeys.POKEMON_PROPERTIES_MOVESET_BUILDERS)?.asString?.split(",")?.map { it.asIdentifierDefaultingNamespace() }
heldItem = json.get(DataKeys.POKEMON_PROPERTIES_HELDITEM)?.asString
heldItem = json.getAsJsonObject(DataKeys.POKEMON_PROPERTIES_HELDITEM)?.let {
ItemStack.SINGLE_ITEM_CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first
}
cosmeticItem = json.getAsJsonObject(DataKeys.POKEMON_PROPERTIES_COSMETICITEM)?.let {
ItemStack.SINGLE_ITEM_CODEC.decode(JsonOps.INSTANCE, it).result().getOrNull()?.first
}
scaleModifier = json.get(DataKeys.POKEMON_SCALE_MODIFIER)?.asFloat
val custom = json.get(DataKeys.POKEMON_PROPERTIES_CUSTOM)?.asJsonArray
// This is still kinda gross
@@ -859,6 +907,7 @@ open class PokemonProperties {
moves?.let { pieces.add("moves=${it.joinToString(separator = ",")}") }
movesetBuilders?.let { pieces.add("movesetbuilders=${it.joinToString(separator = ",") { source -> source.toString() }}") }
heldItem?.let {pieces.add("helditem=$it")}
cosmeticItem?.let {pieces.add("cosmeticitem=$it")}
scaleModifier?.let { pieces.add("scale_modifier=$it") }
return pieces.joinToString(separator)
}
@@ -9,9 +9,12 @@
package com.cobblemon.mod.common.api.riding.stats
import com.cobblemon.mod.common.api.cooking.Flavour
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.util.lang
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.tags.TagKey
import net.minecraft.world.item.Item
/**
* A stat that is used for riding Pokémon. The exact implementation of each of these depends on the [com.cobblemon.mod.common.api.riding.controller.RideController].
@@ -20,13 +23,14 @@ import net.minecraft.network.chat.MutableComponent
* @since February 17th, 2025
*/
enum class RidingStat(
val flavour: Flavour
val flavour: Flavour,
val powerItem: TagKey<Item>
) {
ACCELERATION(Flavour.SPICY),
SKILL(Flavour.DRY),
SPEED(Flavour.SWEET),
STAMINA(Flavour.SOUR),
JUMP(Flavour.BITTER);
ACCELERATION(Flavour.SPICY, CobblemonItemTags.POWER_BRACER),
SKILL(Flavour.DRY, CobblemonItemTags.POWER_BELT),
SPEED(Flavour.SWEET, CobblemonItemTags.POWER_ANKLET),
STAMINA(Flavour.SOUR, CobblemonItemTags.POWER_WEIGHT),
JUMP(Flavour.BITTER, CobblemonItemTags.POWER_BAND);
companion object {
fun getByName(name: String) = RidingStat.entries.firstOrNull { it.name.equals(name, ignoreCase = true) }
@@ -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://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.command.argument.PartySlotArgumentType
import com.cobblemon.mod.common.util.commandLang
import com.cobblemon.mod.common.util.permission
import com.mojang.brigadier.Command
import com.mojang.brigadier.CommandDispatcher
import com.mojang.brigadier.context.CommandContext
import net.minecraft.commands.CommandBuildContext
import net.minecraft.commands.CommandSourceStack
import net.minecraft.commands.Commands.argument
import net.minecraft.commands.Commands.literal
import net.minecraft.commands.arguments.EntityArgument
import net.minecraft.commands.arguments.item.ItemArgument
object CosmeticItemCommand {
private const val NAME = "cosmetic_item"
private const val TARGET = "target"
private const val SLOT = "slot"
private const val ITEM = "item"
fun register(dispatcher: CommandDispatcher<CommandSourceStack>, commandRegistryAccess: CommandBuildContext) {
dispatcher.register(literal(NAME)
.permission(CobblemonPermissions.COSMETIC_ITEM)
.then(argument(TARGET, EntityArgument.player())
.then(argument(SLOT, PartySlotArgumentType.partySlot())
.then(argument(ITEM, ItemArgument.item(commandRegistryAccess))
.executes(this::execute)
)
)
)
)
}
private fun execute(ctx: CommandContext<CommandSourceStack>): Int {
val player = EntityArgument.getPlayer(ctx, TARGET)
val pokemon = PartySlotArgumentType.getPokemonOf(ctx, SLOT, player)
val stackArgument = ItemArgument.getItem(ctx, ITEM)
val stack = stackArgument.createItemStack(1, false)
pokemon.swapCosmeticItem(stack, true)
ctx.source.sendSuccess({ commandLang(NAME, player.name, pokemon.species.translatedName, stack.displayName) }, true)
return Command.SINGLE_SUCCESS
}
}
@@ -47,6 +47,10 @@ class CobblemonConfig {
@IntConstraint(min = 0, max = 1000)
var maxPokemonFriendship = 255
@CobblemonConfigField(Category.Pokemon, lang = "power_item_ride_stat_gain_interval_seconds", SERVER)
@IntConstraint(min = -1, max = Int.MAX_VALUE)
var powerItemRideStatGainIntervalSeconds = 120
@CobblemonConfigField(Category.Pokemon, lang = "announce_drop_items", SERVER)
var announceDropItems = true
@CobblemonConfigField(Category.Pokemon, lang = "default_drop_item_method", SERVER)
@@ -107,6 +107,7 @@ import com.cobblemon.mod.common.pokemon.feature.SlowpokeTailRegrowthSpeciesFeatu
import com.cobblemon.mod.common.pokemon.feature.StashHandler
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import com.google.common.collect.UnmodifiableIterator
@@ -1296,17 +1297,11 @@ open class PokemonEntity(
private fun showInteractionWheel(player: ServerPlayer, itemStack: ItemStack) {
val canRide = isRideableFor(player)
if (pokemon.getOwnerPlayer() == player) {
val cosmeticItemDefinition = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(
player.level().registryAccess(),
pokemon,
itemStack
)
InteractPokemonUIPacket(
this.getUUID(),
canSitOnShoulder() && pokemon in player.party(),
!(pokemon.heldItemNoCopy().isEmpty && itemStack.isEmpty),
(!pokemon.cosmeticItem.isEmpty && itemStack.isEmpty) || cosmeticItemDefinition != null,
canGiveCosmetic(itemStack),
canRide
).sendToPlayer(player)
}
@@ -1328,6 +1323,17 @@ open class PokemonEntity(
return@canRide behaviour.isActive(settings, state, this)
}
fun canGiveCosmetic(itemStack: ItemStack): Boolean {
val cosmeticItemDefinition = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(
RegistryAccessResolver.getAccess(),
pokemon,
itemStack
)
return (!pokemon.cosmeticItem().isEmpty && itemStack.isEmpty)
|| (itemStack.has(CobblemonItemComponents.ASPECT) || cosmeticItemDefinition != null)
}
override fun getDimensions(pose: Pose): EntityDimensions {
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.effectiveScale)
var result = this.exposedForm.hitbox.scale(scale)
@@ -1560,7 +1566,7 @@ open class PokemonEntity(
return false
}
val possibleReturn = if (isCosmetic) this.pokemon.cosmeticItem.copy() else this.pokemon.heldItemNoCopy()
val possibleReturn = if (isCosmetic) this.pokemon.cosmeticItem() else this.pokemon.heldItemNoCopy()
val giving = stack.copy().apply { count = 1 }
if (ItemStack.isSameItemSameComponents(giving, possibleReturn)) {
@@ -2749,4 +2755,4 @@ open class PokemonEntity(
if (this.pokemon.species.silenceStepSounds) return
super.playStepSound(pos, state)
}
}
}
@@ -22,6 +22,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.feature.TickingSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.spawning.fishing.FishingSpawnCause.Companion.DROPS_REROLL_ASPECT
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.battles.BattleRegistry
@@ -46,10 +47,15 @@ import kotlin.math.*
/** Handles purely server logic for a Pokémon */
class PokemonServerDelegate : PokemonSideDelegate {
companion object {
private const val RIDE_STAT_GAIN_AMOUNT = 1F
}
lateinit var entity: PokemonEntity
var acknowledgedHPValue = -1
var acknowledgedLevel = -1
val passengerOffsets = mutableMapOf<Int, Vec3>()
private var rideStatGainAnchor: Vec3? = null
/** Mocked properties exposed to the client [PokemonEntity]. */
private val mock: PokemonProperties?
@@ -355,9 +361,30 @@ class PokemonServerDelegate : PokemonSideDelegate {
}
}
val rideStatGainIntervalSeconds = Cobblemon.config.powerItemRideStatGainIntervalSeconds
if (rideStatGainIntervalSeconds > 0) {
val rideStatGainIntervalTicks = rideStatGainIntervalSeconds * 20L
if (entity.level().gameTime % rideStatGainIntervalTicks == 0L) {
trainRideStats()
}
}
updateTrackedValues()
}
private fun trainRideStats() {
val position = entity.position()
val anchor = rideStatGainAnchor
rideStatGainAnchor = position
if (anchor == null || anchor == position || entity.passengers.isEmpty()) return
val heldItem = entity.pokemon.heldItemNoCopy()
if (heldItem.isEmpty) return
val stat = RidingStat.entries.firstOrNull { heldItem.`is`(it.powerItem) } ?: return
entity.pokemon.addRideBoost(stat, RIDE_STAT_GAIN_AMOUNT)
}
fun updateShownItem() {
val trackedShownItem = when {
// Show Hand Item if Held item is hidden
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.item.components
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.mojang.serialization.Codec
import io.netty.buffer.ByteBuf
import net.minecraft.core.registries.Registries
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.util.StringRepresentable
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import java.util.Locale.getDefault
import kotlin.jvm.optionals.getOrNull
class BedrockAspectComponent(val aspect: String) {
enum class Type: StringRepresentable {
HELD,
COSMETIC;
override fun toString(): String = name.lowercase(getDefault())
override fun getSerializedName(): String = name.lowercase(getDefault())
}
companion object {
val CODEC: Codec<BedrockAspectComponent> = Codec.STRING.xmap(
{ aspect -> BedrockAspectComponent(aspect) },
{ component -> component.aspect }
)
val PACKET_CODEC: StreamCodec<ByteBuf, BedrockAspectComponent> = ByteBufCodecs.fromCodec(CODEC)
fun getAspect(stack: ItemStack, type: Type): String {
val identifier = RegistryAccessResolver.getAccess()
.registry(Registries.ITEM)
.getOrNull()
?.getResourceKey(stack.item)
?.getOrNull()
?.location()
?.toString()
?: "minecraft:dirt"
val item = identifier.replace("\\W".toRegex(), "-")
return when (stack.has(CobblemonItemComponents.ASPECT)) {
true -> {
val aspect = stack[CobblemonItemComponents.ASPECT]!!.aspect.replace("\\W".toRegex(), "-")
"${type}_${item}-${aspect}"
}
false -> "${type}_$item"
}
}
fun setAspect(stack: ItemStack, aspect: String) = stack.also {
it.set(CobblemonItemComponents.ASPECT, BedrockAspectComponent(aspect))
}
fun createStack(item: Item, aspect: String): ItemStack = item.defaultInstance.also {
setAspect(it, aspect)
}
}
}
@@ -22,9 +22,7 @@ class CosmeticItemUpdatePacket(pokemon: () -> Pokemon?, value: ItemStack) : Sing
buffer.writeItemStack(this.value)
}
override fun set(pokemon: Pokemon, value: ItemStack) {
pokemon.cosmeticItem = value
}
override fun set(pokemon: Pokemon, value: ItemStack) { pokemon.swapCosmeticItem(this.value, false) }
companion object {
val ID = cobblemonResource("cosmetic_item_update")
@@ -34,4 +32,4 @@ class CosmeticItemUpdatePacket(pokemon: () -> Pokemon?, value: ItemStack) : Sing
return CosmeticItemUpdatePacket(pokemon, cosmeticItem)
}
}
}
}
@@ -34,4 +34,4 @@ class HeldItemUpdatePacket(pokemon: () -> Pokemon?, value: ItemStack): SingleUpd
}
}
}
}
@@ -81,6 +81,7 @@ import com.cobblemon.mod.common.entity.PlatformType
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
import com.cobblemon.mod.common.item.components.BedrockAspectComponent
import com.cobblemon.mod.common.net.messages.client.PokemonUpdatePacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
@@ -159,6 +160,7 @@ import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.MobSpawnType
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
import net.minecraft.world.level.Level
import net.minecraft.world.level.block.CactusBlock
import net.minecraft.world.level.block.CampfireBlock
@@ -1290,20 +1292,23 @@ open class Pokemon : ShowdownIdentifiable {
fun swapHeldItem(stack: ItemStack, decrement: Boolean = true, aiCanDrop: Boolean = true): ItemStack {
val existing = this.heldItem()
val event = HeldItemEvent.Pre(this, stack, existing, decrement)
if (!isClient) {
CobblemonEvents.HELD_ITEM_PRE.post(event)
}
if (!isClient) CobblemonEvents.HELD_ITEM_PRE.post(event)
if (!event.isCanceled) {
val giving = event.receiving.copy().apply { count = 1 }
if (event.decrement) {
event.receiving.shrink(1)
}
this.heldItem = giving
this.canDropHeldItem = giving.isEmpty || aiCanDrop
onChange(HeldItemUpdatePacket({ this }, giving))
this.heldItem = event.receiving.copy()
.apply { count = 1 }
.also { if (event.decrement) event.receiving.shrink(1) }
this.canDropHeldItem = this.heldItem.isEmpty || aiCanDrop
onChange(HeldItemUpdatePacket({ this }, this.heldItem()))
CobblemonEvents.HELD_ITEM_POST.post(HeldItemEvent.Post(this, this.heldItem(), event.returning.copy(), event.decrement)) {
StashHandler.giveHeldItem(it)
}
updateAspects()
return event.returning
}
return stack
@@ -1332,16 +1337,18 @@ open class Pokemon : ShowdownIdentifiable {
fun swapCosmeticItem(stack: ItemStack, decrement: Boolean = true): ItemStack {
val existing = this.cosmeticItem.copy()
val event = HeldItemEvent.Pre(this, stack, existing, decrement)
if (!isClient) {
CobblemonEvents.COSMETIC_ITEM_PRE.post(event)
}
if (!isClient) CobblemonEvents.COSMETIC_ITEM_PRE.post(event)
if (!event.isCanceled) {
val giving = event.receiving.copy().apply { count = 1 }
if (event.decrement) {
event.receiving.shrink(1)
}
this.cosmeticItem = giving
CobblemonEvents.COSMETIC_ITEM_POST.post(HeldItemEvent.Post(this, this.cosmeticItem.copy(), event.returning.copy(), event.decrement))
this.cosmeticItem = event.receiving.copy()
.apply { count = 1 }
.also { if (event.decrement) event.receiving.shrink(1) }
CobblemonEvents.COSMETIC_ITEM_POST.post(HeldItemEvent.Post(this, this.cosmeticItem(), event.returning.copy(), event.decrement))
updateAspects()
return event.returning
}
return stack
@@ -8,18 +8,17 @@
package com.cobblemon.mod.common.pokemon.aspects
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonCosmeticItems
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.api.pokemon.aspect.AspectProvider
import com.cobblemon.mod.common.api.pokemon.aspect.SingleConditionalAspectProvider
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.item.components.BedrockAspectComponent
import com.cobblemon.mod.common.pokemon.Characteristic
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.Nature
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.getMemorySafely
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.server
val SHINY_ASPECT = object : SingleConditionalAspectProvider {
@@ -43,12 +42,35 @@ val GENDER_ASPECT = object : AspectProvider {
val COSMETIC_SLOT_ASPECT = object : AspectProvider {
override fun provide(pokemon: Pokemon): Set<String> {
val server = server() ?: return emptySet()
server() ?: return emptySet()
if (pokemon.cosmeticItem.isEmpty) {
return emptySet()
}
val cosmeticItem = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(server.registryAccess(), pokemon, pokemon.cosmeticItem)
return cosmeticItem?.aspects?.toSet() ?: emptySet()
val cosmeticItemAspectSet = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(
RegistryAccessResolver.getAccess(),
pokemon,
pokemon.cosmeticItem
)?.aspects?.toSet() ?: emptySet()
val stackAspect = BedrockAspectComponent.getAspect(pokemon.cosmeticItem, BedrockAspectComponent.Type.COSMETIC)
return setOf(stackAspect).plus(cosmeticItemAspectSet)
}
override fun provide(properties: PokemonProperties) = emptySet<String>()
}
val HELD_ITEM_ASPECT = object : AspectProvider {
override fun provide(pokemon: Pokemon): Set<String> {
server() ?: return emptySet()
if (pokemon.heldItem.isEmpty) {
return emptySet()
}
return setOf(BedrockAspectComponent.getAspect(pokemon.heldItem, BedrockAspectComponent.Type.HELD))
}
override fun provide(properties: PokemonProperties) = emptySet<String>()
@@ -118,18 +140,18 @@ val CHARACTERISTIC_RAINBOW_ASPECT = object : AspectProvider {
-> "rainbow-green"
(Stats.SPECIAL_DEFENCE to Stats.SPECIAL_ATTACK),
(Stats.SPECIAL_ATTACK to Stats.SPECIAL_DEFENCE)
(Stats.SPECIAL_ATTACK to Stats.SPECIAL_DEFENCE)
-> "rainbow-cyan"
(Stats.SPECIAL_ATTACK to Stats.SPECIAL_ATTACK),
(Stats.SPECIAL_ATTACK to Stats.HP),
(null to Stats.SPECIAL_ATTACK),
(Stats.SPECIAL_DEFENCE to Stats.SPEED),
(Stats.SPEED to Stats.SPECIAL_DEFENCE)
(Stats.SPEED to Stats.SPECIAL_DEFENCE)
-> "rainbow-blue"
(Stats.SPECIAL_ATTACK to Stats.SPEED),
(Stats.SPEED to Stats.SPECIAL_ATTACK)
(Stats.SPEED to Stats.SPECIAL_ATTACK)
-> "rainbow-purple"
(Stats.SPEED to Stats.SPEED),
@@ -143,7 +165,7 @@ val CHARACTERISTIC_RAINBOW_ASPECT = object : AspectProvider {
(Stats.ATTACK to Stats.SPEED),
-> "rainbow-pink"
(null to Stats.HP)
(null to Stats.HP)
-> "rainbow-light-blue"
else -> null
@@ -14,20 +14,22 @@ import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeature
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.server.level.ServerPlayer
import kotlin.jvm.optionals.getOrNull
object StashHandler {
fun interactMob(player: Player, pokemon: Pokemon, itemStack: ItemStack?): Boolean {
if (itemStack == null || player !is ServerPlayer|| pokemon.getOwnerPlayer() !== player) return false
val success = handleItem(pokemon, itemStack.item)
if (success) {
itemStack.shrink(1)
}
return success
return success.also { if (it) itemStack.shrink(1) }
}
fun giveHeldItem(event: HeldItemEvent.Post) {
@@ -40,11 +42,19 @@ object StashHandler {
}
fun handleItem(pokemon: Pokemon, item: Item): Boolean {
val itemIdentifier: ResourceLocation = item.builtInRegistryHolder().key().location()
val itemIdentifier: ResourceLocation = RegistryAccessResolver.getAccess()
.registry(Registries.ITEM)
.get()
.getResourceKey(item)
.get()
.location()
val speciesFeatureProviders = SpeciesFeatures.getFeaturesFor(pokemon.species)
val relevantSpeciesFeatureProviders: List<IntSpeciesFeatureProvider> = speciesFeatureProviders.filter {
it is IntSpeciesFeatureProvider && it.itemPoints.keys.contains(itemIdentifier)
}.map { it as IntSpeciesFeatureProvider }
val relevantSpeciesFeatureProviders: List<IntSpeciesFeatureProvider> = speciesFeatureProviders
.filter { it is IntSpeciesFeatureProvider && it.itemPoints.keys.contains(itemIdentifier) }
.map { it as IntSpeciesFeatureProvider }
for (relevantSpeciesFeatureProvider in relevantSpeciesFeatureProviders) {
val points = relevantSpeciesFeatureProvider.itemPoints[itemIdentifier]!!
val feature = pokemon.getFeature<IntSpeciesFeature>(relevantSpeciesFeatureProvider.keys[0])!!
@@ -52,9 +62,11 @@ object StashHandler {
pokemon.markFeatureDirty(feature)
if (feature.value > relevantSpeciesFeatureProvider.max) feature.value = relevantSpeciesFeatureProvider.max
}
if (relevantSpeciesFeatureProviders.isNotEmpty()) {
if (pokemon.entity != null) pokemon.entity!!.playSound(CobblemonSounds.GIMMIGHOUL_GIVE_ITEM, 1f, 1f)
if (relevantSpeciesFeatureProviders.isNotEmpty() && pokemon.entity != null) {
pokemon.entity!!.playSound(CobblemonSounds.GIMMIGHOUL_GIVE_ITEM, 1f, 1f)
}
return relevantSpeciesFeatureProviders.isNotEmpty()
}
}
}
@@ -24,6 +24,7 @@ import com.cobblemon.mod.common.net.messages.client.data.PropertiesCompletionReg
import com.cobblemon.mod.common.pokemon.EVs
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.IVs
import com.cobblemon.mod.common.pokemon.cosmetic.CosmeticItemDefinition
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.simplify
@@ -151,6 +152,7 @@ internal object PropertiesCompletionProvider : DataRegistry {
this.inject(setOf("originaltrainertype", "ottype"), setOf("None", "Player", "NPC"))
this.inject(setOf("nickname", "nick"), setOf(""))
this.inject(setOf("helditem", "held_item"), setOf("cobblemon:exp_share"))
this.inject(setOf("cosmetic", "cosmeticitem", "cosmetic_item"), setOf("cobblemon:exp_share"))
this.inject(setOf("min_perfect_ivs"), setOf("0", "6"))
this.inject(setOf("scale_modifier"), setOf("0.5", "2.0"))
@@ -89,6 +89,7 @@ object DataKeys {
const val POKEMON_PROPERTIES_MOVES = "Moves"
const val POKEMON_PROPERTIES_HELDITEM = "HeldItem"
const val POKEMON_PROPERTIES_COSMETICITEM = "CosmeticItem"
const val POKEMON_PROPERTIES_MOVESET_BUILDERS = "MovesetBuilders"
// Entity effects
@@ -456,6 +456,12 @@
"cannon_left": {
"position": [0, 0, 3]
}
},
"timeline": {
"0.25": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.25": "q.run_molang('cobblemon:stepsound_standard_large');",
"2.25": "q.run_molang('cobblemon:stepsound_standard_large');",
"3.25": "q.run_molang('cobblemon:stepsound_standard_large');"
}
},
"animation.blastoise.ground_run": {
@@ -83,6 +83,7 @@
},
"animation.parasect.ground_walk": {
"loop": true,
"animation_length": 1,
"bones": {
"body": {
"rotation": [0, 0, 0],
@@ -134,6 +135,12 @@
"locator_seat_1": {
"rotation": [7.5, 0, 0]
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_minecraft');",
"0.25": "q.run_molang('cobblemon:stepsound_minecraft');",
"0.5": "q.run_molang('cobblemon:stepsound_minecraft');",
"0.75": "q.run_molang('cobblemon:stepsound_minecraft');"
}
},
"animation.parasect.ground_run": {
@@ -258,6 +265,10 @@
"rotation": ["5+Math.cos((query.anim_time) * 90 * 16 - 90) * 2", 0, 0],
"position": [0, "0+Math.cos((query.anim_time) * 90 * 32) * -0.2", -0.25]
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_minecraft');",
"0.125": "q.run_molang('cobblemon:stepsound_minecraft');"
}
},
"animation.parasect.ride_ground_run": {
@@ -391,6 +391,10 @@
"0.7083": [0, 0, 0]
}
}
},
"timeline": {
"0.0833": "q.run_molang('cobblemon:stepsound_standard_medium');",
"0.2917": "q.run_molang('cobblemon:stepsound_standard_medium');"
}
},
"animation.dewgong.water_idle": {
@@ -1071,6 +1071,16 @@
"head": {
"rotation": ["-0.8*math.cos(q.anim_time*90*8-120)", 0, "3*math.sin(q.anim_time*90*4+70)"]
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"0.5": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.5": "q.run_molang('cobblemon:stepsound_standard_large');",
"2.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"2.5": "q.run_molang('cobblemon:stepsound_standard_large');",
"3.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"3.5": "q.run_molang('cobblemon:stepsound_standard_large');"
}
},
"animation.rhyhorn.ride_ground_walk": {
@@ -1917,6 +1927,16 @@
],
"position": [0, "0.4+0.6*math.cos(q.anim_time*90*8-120)", 0]
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"0.375": "q.run_molang('cobblemon:stepsound_standard_medium');",
"0.5": "q.run_molang('cobblemon:stepsound_standard_large');",
"0.875": "q.run_molang('cobblemon:stepsound_standard_medium');",
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.375": "q.run_molang('cobblemon:stepsound_standard_medium');",
"1.5": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.875": "q.run_molang('cobblemon:stepsound_standard_medium');"
}
},
"animation.rhyhorn.ride_ground_run": {
@@ -329,6 +329,12 @@
"locator_seat_1": {
"rotation": [-15, 0, 0]
}
},
"timeline": {
"0.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"2.0": "q.run_molang('cobblemon:stepsound_standard_large');",
"3.0": "q.run_molang('cobblemon:stepsound_standard_large');"
}
},
"animation.rhydon.ride_ground_walk": {
@@ -807,6 +813,12 @@
"locator_seat_1": {
"rotation": [-15, 0, 0]
}
},
"timeline": {
"0.3333": "q.run_molang('cobblemon:stepsound_standard_large');",
"0.8333": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.3333": "q.run_molang('cobblemon:stepsound_standard_large');",
"1.8333": "q.run_molang('cobblemon:stepsound_standard_large');"
}
},
"animation.rhydon.ride_ground_run": {
@@ -391,6 +391,10 @@
"1.0": [0, 0, 0]
}
}
},
"timeline": {
"0.0833": "q.run_molang('cobblemon:stepsound_minecraft');",
"0.5833": "q.run_molang('cobblemon:stepsound_minecraft');"
}
},
"animation.mr_mime.ground_run": {
@@ -2381,6 +2381,7 @@
"cobblemon.command.spawnpokemon.nospecies": "Error: You need to include a Pokémon name or 'random'",
"cobblemon.command.error.nonpc": "Error: You need to include a valid NPC identifier.",
"cobblemon.command.held_item": "%1$s's %2$s was given the %3$s to hold.",
"cobblemon.command.cosmetic_item": "%1$s's %2$s was given the %3$s to wear.",
"cobblemon.command.giveallmarks": "%1$s's %2$s was given all available marks.",
"cobblemon.command.givemark": "%1$s's %2$s was given a %3$s.",
"cobblemon.command.givemark.already_has": "%1$s's %2$s already has a %3$s!",
@@ -2434,6 +2435,8 @@
"cobblemon.config.ui.max_pokemon_level.tooltip": "The maximum level Pokémon can get to.",
"cobblemon.config.ui.max_pokemon_friendship": "Max Pokémon Friendship",
"cobblemon.config.ui.max_pokemon_friendship.tooltip": "The maximum friendship value.",
"cobblemon.config.ui.power_item_ride_stat_gain_interval_seconds": "Power Item Riding Stat Training Interval (Seconds)",
"cobblemon.config.ui.power_item_ride_stat_gain_interval_seconds.tooltip": "Seconds between riding stat gains while riding a Pokémon holding a Power item. Defaults to 120 seconds. Set to -1 or 0 to disable; values below -1 are clamped to -1.",
"cobblemon.config.ui.announce_drop_items": "Announce Drop Items",
"cobblemon.config.ui.announce_drop_items.tooltip": "Whether to notify the Player of dropped items from defeating wild Pokémon.",
"cobblemon.config.ui.default_drop_item_method": "Default Drop Item Method",
@@ -1653,6 +1653,18 @@
"move.horndrill.target_2": {
"sounds": [ { "name": "cobblemon:move/horndrill/horndrill_target_2", "volume": 1.0 } ]
},
"move.gigatonhammer.actor": {
"sounds": [ { "name": "cobblemon:move/gigatonhammer/gigatonhammer_actor", "volume": 1.0 } ]
},
"move.gigatonhammer.target": {
"sounds": [ { "name": "cobblemon:move/gigatonhammer/gigatonhammer_target", "volume": 1.0 } ]
},
"move.pyroball.actor": {
"sounds": [ { "name": "cobblemon:move/pyroball/pyroball_actor", "volume": 1.0 } ]
},
"move.pyroball.target": {
"sounds": [ { "name": "cobblemon:move/pyroball/pyroball_target", "volume": 1.0 } ]
},
"impact.normal": {
"sounds": [
{ "name": "cobblemon:move/impact_generic/impact_generic_fighting_1", "volume": 0.8 },
@@ -8672,6 +8684,9 @@
"pokemon.cinderace.cry": {
"sounds": [ { "name": "cobblemon:pokemon/cinderace/cinderace_cry", "volume": 0.8 } ]
},
"pokemon.cinderace.pyroball": {
"sounds": [ { "name": "cobblemon:pokemon/cinderace/cinderace_pyroball", "volume": 0.5 } ]
},
"pokemon.sobble.cry": {
"sounds": [ { "name": "cobblemon:pokemon/sobble/sobble_cry", "volume": 0.8 } ]
},
@@ -0,0 +1,35 @@
{
"timeline": [
{
"type": "add_holds",
"holds": ["effects"]
},
{
"type": "animation",
"animation": ["gigatonhammer"],
"delay": 0.3
},
{
"type": "entity_molang",
"entityCondition": "q.entity.is_user == true",
"expressions": ["q.sound('move.gigatonhammer.actor');"]
},
{
"type": "pause",
"pause": 1.1
},
{
"type": "entity_molang",
"entityCondition": "q.entity.is_user == false && q.missed(q.entity.uuid) == false",
"expressions": ["q.sound('move.gigatonhammer.target');"]
},
{
"type": "remove_holds",
"holds": ["effects"]
},
{
"type": "pause",
"pause": 1.5
}
]
}
@@ -3,11 +3,38 @@
{
"type": "add_holds",
"holds": ["effects"]
},
{
"type": "animation",
"animation": ["pyroball"],
"delay": 2.05
"animation": ["pyroball"]
},
{
"type": "pause",
"pause" : 0.1
},
{
"type": "entity_molang",
"entityCondition": "q.entity.is_user == true",
"expressions": ["q.sound('move.pyroball.actor');"]
},
{
"type": "entity_molang",
"entityCondition": "q.entity.is_user == true",
"expressions": ["q.sound('pokemon.cinderace.pyroball');"]
},
{
"type": "pause",
"pause" : 1.875
},
{
"type": "entity_molang",
"entityCondition": "q.entity.is_user == false && q.missed(q.entity.uuid) == false",
"expressions": ["q.sound('move.pyroball.target');"]
},
{
"type": "pause",
"pause" : 0.175
},
{
"type": "entity_particles",
@@ -14,7 +14,7 @@
}
],
"type": "cobblemon:cooking_pot_shapeless",
"category": "medicines",
"category": "misc",
"ingredients": [
{
"item": "create:experience_nugget"
@@ -6,6 +6,7 @@
"maleRatio": 0.875,
"height": 16,
"weight": 855,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.blastoise.desc"
],
@@ -7,6 +7,7 @@
"maleRatio": 0.5,
"height": 17,
"weight": 1200,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.dewgong.desc"
],
@@ -7,6 +7,7 @@
"maleRatio": 0.5,
"height": 13,
"weight": 545,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.mrmime.desc"
],
@@ -7,6 +7,7 @@
"maleRatio": 0.5,
"height": 10,
"weight": 295,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.parasect.desc"
],
@@ -7,6 +7,7 @@
"maleRatio": 0.5,
"height": 19,
"weight": 1200,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.rhydon.desc"
],
@@ -7,6 +7,7 @@
"maleRatio": 0.5,
"height": 10,
"weight": 1150,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.rhyhorn.desc"
],
@@ -7,6 +7,7 @@
"maleRatio": 0.875,
"height": 20,
"weight": 1000,
"silenceStepSounds": true,
"pokedex": [
"cobblemon.species.venusaur.desc"
],
@@ -6,6 +6,7 @@
"cobblemon:tart_apple",
"cobblemon:syrupy_apple",
"cobblemon:candied_apple",
{ "id": "create:honeyed_apple","required": false }
{ "id": "create:honeyed_apple","required": false },
{ "id": "nomansland:honeyed_apple","required": false }
]
}