Compare commits

...
Author SHA1 Message Date
Gitoido 5b0c34ce9a feat+refactor: add separate molang script modifier and rename og molang modifier to expression 2026-10-11 14:17:09 +03:00
Gitoido 37f70903c5 refactor: use java.awt.Color as beam color
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
index 76f32afff4..a4297de4c4 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
@@ -37,6 +37,7 @@ import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
 import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
 import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
 import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
+import com.cobblemon.mod.common.util.adapters.LiteralHexColorAdapter
 import com.cobblemon.mod.common.util.asExpressionLike
 import com.cobblemon.mod.common.util.cobblemonResource
 import com.google.gson.Gson
@@ -47,6 +48,7 @@ import net.minecraft.resources.ResourceLocation
 import net.minecraft.server.level.ServerPlayer
 import net.minecraft.server.packs.PackType
 import net.minecraft.util.LowerCaseEnumTypeAdapterFactory
+import java.awt.Color
 import kotlin.math.roundToInt

 /**
@@ -68,6 +70,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         .registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
         .registerTypeAdapter(CatchRateModifier::class.java, CobblemonCatchRateModifierAdapter)
         .registerTypeAdapter(CaptureEffect::class.java, CobblemonCaptureEffectAdapter)
+        .registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
         .registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
         .disableHtmlEscaping()
         .setPrettyPrinting()
@@ -384,7 +387,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         model3d: ResourceLocation = cobblemonResource("item/${name}_model"),
         throwPower: Float = 1.25f,
         ancient: Boolean = false,
-        color: String = "#ff1111"
+        color: Color = Color.decode("#ff1111")
     ): PokeBall {
         val identifier = cobblemonResource(name)
         val pokeball = PokeBall(identifier, modifier, effects, PokeBall.PokeBallProperties(), waterDragValue, model2d, model3d, throwPower, ancient, color)
@@ -396,6 +399,4 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         val identifier = cobblemonResource(name)
         return this.custom[identifier] ?: this.defaults[identifier]!!
     }
-
-    fun isAncient(identifier: ResourceLocation) = this.getPokeBall(identifier).ancient
 }
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt i/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
index cf2b05173c..a82ada3eda 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
@@ -56,6 +56,6 @@ class EmptyPokeBallClientDelegate : PokeBallPosableState(), EntitySideDelegate<E
     }

     fun getBeamColor(): Vector4f {
-        return Integer.decode(currentEntity.pokeBall.beamColor).toRGBA()
+        return currentEntity.pokeBall.beamColor.rgb.toRGBA()
     }
 }
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt i/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
index 70538ec1af..ed39f7ed1e 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
@@ -325,7 +325,7 @@ class PokemonRenderer(

         val beamColorVec4f = when(beamTarget) {
             is EmptyPokeBallEntity -> (beamTarget.delegate as EmptyPokeBallClientDelegate).getBeamColor()
-            else -> Integer.decode(entity.pokemon.caughtBall.beamColor).toRGBA()
+            else -> entity.pokemon.caughtBall.beamColor.rgb.toRGBA()
         }

         val beamSourcePosition = when {
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt i/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
index 638ef99a98..d159237173 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
@@ -23,6 +23,7 @@ import net.minecraft.resources.ResourceLocation
 import net.minecraft.world.item.Item
 import net.minecraft.world.item.ItemStack
 import net.minecraft.world.item.Rarity
+import java.awt.Color

 /**
  * Base poke ball object
@@ -48,17 +49,10 @@ class PokeBall(
     val model3d: ResourceLocation,
     val throwPower: Float,
     val ancient: Boolean = false,
-    val beamColor: String
+    val beamColor: Color
 ) {
-    init {
-        // color needs to be a hex string
-        if (!"""#[a-zA-Z0-9]{6}""".toRegex().matches(beamColor)) {
-            throw RuntimeException("")
-        }
-    }
-
     @Transient
-    val struct = ObjectValue<PokeBall>(this)
+    val struct = ObjectValue(this)
         .addPokeBallFunctions(this)
 //        .addFunction("item") {  } // requires a registry which is hard
2026-10-11 14:17:07 +03:00
Gitoido 1149880926 chore: cleanup PokeBallComponent 2026-10-11 14:16:37 +03:00
Gitoido 42d569b4fa refactor: caught ball fallback to strange ball in case of datapack removal 2026-10-11 14:16:36 +03:00
Gitoido f1f1f183f1 feat: shedders evo custom ball support 2026-10-11 14:16:36 +03:00
Gitoido c4fdb4c89e refactor: default to strange ball instead of poke ball for some cases. code cleanup 2026-10-11 14:16:36 +03:00
Gitoido 87510acd17 feat: strange ball i18n 2026-10-11 14:16:36 +03:00
Gitoido dc8821ed8d refactor: update item renderer and model loader mixins to account for custom balls 2026-10-11 14:16:36 +03:00
Gitoido 04478d7807 feat: recall beam now uses correct beam color for ball 2026-10-11 14:16:36 +03:00
Gitoido f708c1bddb feat: molang effects - functional MVP is complete 2026-10-11 14:16:36 +03:00
Gitoido 97b7cf4bee feat: very very very temporary assets for testing purposes 2026-10-11 14:16:36 +03:00
Gitoido 0447ecded7 feat+refactor: add MoLangModifier, remove deprecated poke ball getters 2026-10-11 14:16:36 +03:00
Gitoido ab9e73d3ec fix: add strange ball to poke_balls item tag 2026-10-11 14:16:36 +03:00
Gitoido d34a0d28aa refactor: use correct ball name in battle log 2026-10-11 14:16:36 +03:00
Gitoido d46627d24e fix: drop correct pokeball stack on wall hit/drop 2026-10-11 14:16:36 +03:00
Gitoido 9f5c76fc0b refactor: strange ball defaults for EmptyPokeBallEntity 2026-10-11 14:16:36 +03:00
Gitoido 94e12787a2 feat: add possibility to change beam color of poke ball
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
index 8f50077bca..7180368028 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
@@ -8,6 +8,7 @@

 package com.cobblemon.mod.common.api.pokeball

+import com.cobblemon.mod.common.Cobblemon
 import com.cobblemon.mod.common.api.Priority
 import com.cobblemon.mod.common.api.data.JsonDataRegistry
 import com.cobblemon.mod.common.api.events.CobblemonEvents
@@ -332,7 +333,9 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
     }

     override fun reload(data: Map<ResourceLocation, PokeBall>) {
+        data.entries.forEach { it.value.name = it.key }
         this.custom.clear()
+
         data.forEach { (location, ball) ->
             if (defaults.containsKey(location)) {
                 ball.stack = defaults[location]!!.stack
@@ -342,6 +345,8 @@ object PokeBalls : JsonDataRegistry<PokeBall> {

             this.custom[location] = ball
         }
+
+        Cobblemon.LOGGER.info("Loaded ${data.size} custom poke balls.")
     }

     override fun sync(player: ServerPlayer) {
@@ -367,10 +372,11 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         model2d: ResourceLocation = cobblemonResource(name),
         model3d: ResourceLocation = cobblemonResource("item/${name}_model"),
         throwPower: Float = 1.25f,
-        ancient: Boolean = false
+        ancient: Boolean = false,
+        color: String = "#ff1111"
     ): PokeBall {
         val identifier = cobblemonResource(name)
-        val pokeball = PokeBall(identifier, modifier, effects, PokeBall.PokeBallProperties(), waterDragValue, model2d, model3d, throwPower, ancient)
+        val pokeball = PokeBall(identifier, modifier, effects, PokeBall.PokeBallProperties(), waterDragValue, model2d, model3d, throwPower, ancient, color)
         this.defaults[identifier] = pokeball
         return pokeball
     }
@@ -380,7 +386,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         return this.custom[identifier] ?: this.defaults[identifier]!!
     }

-    fun isAncient(identifier: ResourceLocation) = this.getPokeBall(identifier)?.ancient ?: false
+    fun isAncient(identifier: ResourceLocation) = this.getPokeBall(identifier).ancient

     /**
      * Backwards compatibility getters
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt i/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
index 944dda3995..b8a34288d6 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/client/entity/EmptyPokeBallClientDelegate.kt
@@ -16,7 +16,9 @@ import com.cobblemon.mod.common.client.render.pokeball.PokeBallPosableState
 import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
 import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity.CaptureState
 import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity.CaptureState.NOT
+import com.cobblemon.mod.common.util.toRGBA
 import net.minecraft.network.syncher.EntityDataAccessor
+import org.joml.Vector4f

 class EmptyPokeBallClientDelegate : PokeBallPosableState(), EntitySideDelegate<EmptyPokeBallEntity> {
     override val stateEmitter: SettableObservable<CaptureState> = SettableObservable(NOT)
@@ -52,4 +54,8 @@ class EmptyPokeBallClientDelegate : PokeBallPosableState(), EntitySideDelegate<E
             EmptyPokeBallEntity.ASPECTS -> currentAspects = currentEntity.aspects
         }
     }
-}
\ No newline at end of file
+
+    fun getBeamColor(): Vector4f {
+        return Integer.decode(currentEntity.pokeBall.color).toRGBA()
+    }
+}
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt i/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
index d55550f02a..227a03f4e0 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/client/render/pokemon/PokemonRenderer.kt
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.OrientationControllable
 import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
 import com.cobblemon.mod.common.client.CobblemonClient
 import com.cobblemon.mod.common.client.battle.ClientBallDisplay
+import com.cobblemon.mod.common.client.entity.EmptyPokeBallClientDelegate
 import com.cobblemon.mod.common.client.entity.NPCClientDelegate
 import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
 import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BEAM_EXTEND_TIME
@@ -320,6 +321,11 @@ class PokemonRenderer(
         val pokemonPosition = entity.position().add(0.0, entity.bbHeight / 2.0 * clientDelegate.activeSendoutScale.toDouble(), 0.0)
         val recallBallOrigin = (beamTarget as? Player)?.let { RecallArmAnimationTracker.get(it.uuid)?.beamOrigin }

+        val beamColorVec4f = when(beamTarget) {
+            is EmptyPokeBallEntity -> (beamTarget.delegate as EmptyPokeBallClientDelegate).getBeamColor()
+            else -> recallBeamColour
+        }
+
         val beamSourcePosition = when {
             // cover the recall ball origin presence
             recallBallOrigin != null -> recallBallOrigin
@@ -385,10 +391,10 @@ class PokemonRenderer(
             partialTicks = partialTicks,
             totalLevelTime = entity.level().gameTime,
             height = pokemonPosition.distanceTo(beamSourcePosition.add(offset)).toFloat() * ratio,
-            red = recallBeamColour.x,
-            green = recallBeamColour.y,
-            blue = recallBeamColour.z,
-            alpha = recallBeamColour.w,
+            red = beamColorVec4f.x,
+            green = beamColorVec4f.y,
+            blue = beamColorVec4f.z,
+            alpha = beamColorVec4f.w,
             beamRadius = 0.03F,
             glowRadius = 0.07F,
             glowAlpha = 0.4F
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt i/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
index 77c9bc35d4..98168a053e 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
@@ -19,9 +19,9 @@ import com.cobblemon.mod.common.pokemon.Pokemon
 import com.cobblemon.mod.common.util.cobblemonResource
 import com.cobblemon.mod.common.util.codec.CodecUtils
 import com.mojang.serialization.Codec
-import net.minecraft.world.item.ItemStack
 import net.minecraft.resources.ResourceLocation
 import net.minecraft.world.item.Item
+import net.minecraft.world.item.ItemStack
 import net.minecraft.world.item.Rarity

 /**
@@ -35,9 +35,11 @@ import net.minecraft.world.item.Rarity
  * @property model2d The identifier for the resource this Pokéball will use for the 2d model.
  * @property model3d The identifier for the resource this Pokéball will use for the 3d model.
  * @property ancient Marks this Pokéball as ancient, which makes it play different animations on catch hops
+ * @property color Defines the beam color of pokeball
  */
 class PokeBall(
-    val name: ResourceLocation = cobblemonResource("strange_ball"),
+    @Transient // Assigns in PokeBalls.reload
+    var name: ResourceLocation = cobblemonResource("strange_ball"),
     val catchRateModifier: CatchRateModifier = CatchRateModifier.DUMMY,
     val effects: List<CaptureEffect> = listOf(),
     val itemProperties: PokeBallProperties = PokeBallProperties(),
@@ -45,12 +47,15 @@ class PokeBall(
     val model2d: ResourceLocation,
     val model3d: ResourceLocation,
     val throwPower: Float,
-    val ancient: Boolean = false
+    val ancient: Boolean = false,
+    val color: String
 ) {
-    data class PokeBallProperties(
-        val fireResistant: Boolean = false,
-        val rarity: Rarity = Rarity.COMMON
-    )
+    init {
+        // color needs to be a hex string
+        if (!"""#[a-zA-Z0-9]{6}""".toRegex().matches(color)) {
+            throw RuntimeException("")
+        }
+    }

     @Transient
     val struct = QueryStruct(hashMapOf())
@@ -84,7 +89,6 @@ class PokeBall(
         if (this.name == PokeBalls.SAFARI_BALL.name) target.maxHealth else target.currentHealth

     companion object {
-
         @JvmStatic
         val BY_IDENTIFIER_CODEC: Codec<PokeBall> = CodecUtils.createByIdentifierCodec(
             PokeBalls::getPokeBall,
@@ -92,4 +96,9 @@ class PokeBall(
         ) { identifier -> "No PokeBall for ID $identifier" }

     }
+
+    data class PokeBallProperties(
+        val fireResistant: Boolean = false,
+        val rarity: Rarity = Rarity.COMMON
+    )
 }
2026-10-11 14:16:33 +03:00
Gitoido 2cd0686537 fix: default value for pokeball root bone in its posable state 2026-10-11 14:15:18 +03:00
Gitoido 9c539d7aaf refactor: derive poke ball name from component or lack of thereof 2026-10-11 14:15:18 +03:00
Gitoido 1ee2374923 feat: step 3 - add the Strange Ball, allow reading pokeballs from json, stubs for capture effects
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt i/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
index bb3997bf27..bb67ce206f 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItemComponents.kt
@@ -99,6 +99,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
             .networkSynchronized(ByteBufCodecs.BOOL)
             .build())

+    @JvmField
+    val POKE_BALL: DataComponentType<PokeBallComponent> = create("poke_ball", DataComponentType.builder<PokeBallComponent>()
+        .persistent(PokeBallComponent.CODEC)
+        .networkSynchronized(PokeBallComponent.PACKET_CODEC)
+        .build())
+
     fun register() {
         Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
         Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait"), BAIT)
@@ -112,9 +118,10 @@ 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:poke_ball"), POKE_BALL)
     }

     override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
     override val resourceKey = Registries.DATA_COMPONENT_TYPE

-}
\ No newline at end of file
+}
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt i/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
index 02d78b1c4d..e6ff6f3bf6 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/CobblemonItems.kt
@@ -172,6 +172,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
     val ANCIENT_GIGATON_BALL = pokeBallItem(PokeBalls.ANCIENT_GIGATON_BALL)
     @JvmField
     val ANCIENT_ORIGIN_BALL = pokeBallItem(PokeBalls.ANCIENT_ORIGIN_BALL)
+    @JvmField
+    val STRANGE_BALL = pokeBallItem(PokeBalls.STRANGE_BALL)

     val pokedexes = mutableListOf<PokedexItem>()
     @JvmField
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
index cc7f986bd8..8f50077bca 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/PokeBalls.kt
@@ -21,14 +21,22 @@ import com.cobblemon.mod.common.api.pokemon.stats.Stats
 import com.cobblemon.mod.common.api.pokemon.status.Statuses
 import com.cobblemon.mod.common.api.reactive.SimpleObservable
 import com.cobblemon.mod.common.api.types.ElementalTypes
+import com.cobblemon.mod.common.item.components.PokeBallComponent
 import com.cobblemon.mod.common.pokeball.PokeBall
+import com.cobblemon.mod.common.util.adapters.CobblemonCaptureEffectAdapter
+import com.cobblemon.mod.common.util.adapters.CobblemonCatchRateModifierAdapter
+import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
+import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
+import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
 import com.cobblemon.mod.common.util.cobblemonResource
 import com.google.gson.Gson
 import com.google.gson.GsonBuilder
 import com.google.gson.reflect.TypeToken
+import net.minecraft.advancements.critereon.MinMaxBounds
 import net.minecraft.resources.ResourceLocation
 import net.minecraft.server.level.ServerPlayer
 import net.minecraft.server.packs.PackType
+import net.minecraft.util.LowerCaseEnumTypeAdapterFactory
 import kotlin.math.roundToInt

 /**
@@ -43,6 +51,12 @@ object PokeBalls : JsonDataRegistry<PokeBall> {

     // ToDo once datapack pokeball is implemented add required adapters here
     override val gson: Gson = GsonBuilder()
+        .registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
+        .registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
+        .registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
+        .registerTypeAdapter(CatchRateModifier::class.java, CobblemonCatchRateModifierAdapter)
+        .registerTypeAdapter(CaptureEffect::class.java, CobblemonCaptureEffectAdapter)
+        .registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
         .disableHtmlEscaping()
         .setPrettyPrinting()
         .create()
@@ -50,8 +64,8 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
     override val resourcePath = "pokeballs"

     private val defaults = hashMapOf<ResourceLocation, PokeBall>()
-    // ToDo datapack pokeball type here instead
-    private val custom = hashMapOf<ResourceLocation, PokeBall>()
+
+    val custom = hashMapOf<ResourceLocation, PokeBall>()

     @JvmStatic
     @get:JvmName("getPokeBall")
@@ -245,6 +259,10 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
     @get:JvmName("getAncientOriginBall")
     val ANCIENT_ORIGIN_BALL
         get() = this.byName("ancient_origin_ball")
+    @JvmStatic
+    @get:JvmName("getStrangeBall")
+    val STRANGE_BALL
+        get() = this.byName("strange_ball")

     init {
         createDefault("poke_ball")
@@ -257,7 +275,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         createDefault("ultra_ball", MultiplierModifier(2F))
         createDefault("master_ball", GuaranteedModifier())
         createDefault("safari_ball", CatchRateModifiers.SAFARI)
-        createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
+        createDefault("fast_ball", BaseStatModifier(Stats.SPEED, 100, BaseStatModifier.Comparator.GTE, 4F))
         createDefault("level_ball", CatchRateModifiers.LEVEL)
         createDefault("lure_ball", CatchRateModifiers.LURE)
         createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
@@ -295,6 +313,9 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
         createDefault("ancient_wing_ball", MultiplierModifier(1.5F), throwPower = 2.5f, ancient = true)
         createDefault("ancient_jet_ball", MultiplierModifier(2F), throwPower = 2.5f, ancient = true)
         createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
+        // for customs
+        createDefault("strange_ball")
+
         // Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
         CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
             if (!event.earned) {
@@ -312,7 +333,15 @@ object PokeBalls : JsonDataRegistry<PokeBall> {

     override fun reload(data: Map<ResourceLocation, PokeBall>) {
         this.custom.clear()
-        // ToDo once datapack pokeball is implemented load them here, we will want datapacks to be able to override our default pokeballs too, however they will never be able to disable them
+        data.forEach { (location, ball) ->
+            if (defaults.containsKey(location)) {
+                ball.stack = defaults[location]!!.stack
+            } else {
+                ball.stack = PokeBallComponent.createStack(ball)
+            }
+
+            this.custom[location] = ball
+        }
     }

     override fun sync(player: ServerPlayer) {
@@ -321,10 +350,11 @@ object PokeBalls : JsonDataRegistry<PokeBall> {

     /**
      * Gets a Pokeball from registry name.
-     * @return the pokeball object if found otherwise null.
+     * @return the pokeball object if found otherwise strange ball.
      */
     @JvmStatic
-    fun getPokeBall(name : ResourceLocation): PokeBall? = this.custom[name] ?: this.defaults[name]
+    fun getPokeBall(name : ResourceLocation): PokeBall =
+        this.custom[name] ?: this.defaults[name] ?: STRANGE_BALL

     @JvmStatic
     fun all() = this.defaults.filterKeys { !this.custom.containsKey(it) }.values + this.custom.values
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/effects/FriendshipEarningBoostEffect.kt i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/effects/FriendshipEarningBoostEffect.kt
index 9dffa52c41..1c5f78f4dc 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/effects/FriendshipEarningBoostEffect.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/api/pokeball/catching/effects/FriendshipEarningBoostEffect.kt
@@ -18,6 +18,10 @@ import net.minecraft.world.entity.LivingEntity
  *
  * @property multiplier The multiplier for friendship gains
  */
-class FriendshipEarningBoostEffect(val multiplier: Float) : CaptureEffect {
+data class FriendshipEarningBoostEffect(val multiplier: Float) : CaptureEffect {
+    companion object {
+        const val VARIANT = "friendship_boost"
+    }
+
     override fun apply(thrower: LivingEntity, pokemon: Pokemon) {}
-}
\ No newline at end of file
+}
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/item/components/PokeBallComponent.kt i/common/src/main/kotlin/com/cobblemon/mod/common/item/components/PokeBallComponent.kt
new file mode 100644
index 0000000000..12517ee396
--- /dev/null
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/item/components/PokeBallComponent.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2023 Cobblemon Contributors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at https://mozilla.org/MPL/2.0/.
+ */
+
+package com.cobblemon.mod.common.item.components
+
+import com.cobblemon.mod.common.Cobblemon
+import com.cobblemon.mod.common.CobblemonItemComponents
+import com.cobblemon.mod.common.CobblemonItems
+import com.cobblemon.mod.common.api.pokeball.PokeBalls
+import com.cobblemon.mod.common.pokeball.PokeBall
+import com.mojang.serialization.Codec
+import com.mojang.serialization.codecs.RecordCodecBuilder
+import io.netty.buffer.ByteBuf
+import net.minecraft.network.codec.ByteBufCodecs
+import net.minecraft.network.codec.StreamCodec
+import net.minecraft.resources.ResourceLocation
+import net.minecraft.world.item.ItemStack
+
+data class PokeBallComponent(val name: ResourceLocation) {
+    companion object {
+        // Codec to serialize/deserialize PokeBall
+        val CODEC: Codec<PokeBallComponent> = RecordCodecBuilder.create { instance ->
+            instance.group(
+                ResourceLocation.CODEC.fieldOf("move").forGetter { it.name } // Serialize the move's name
+            ).apply(instance) { name -> PokeBallComponent(name) }
+        }
+
+        val PACKET_CODEC: StreamCodec<ByteBuf, PokeBallComponent> = ByteBufCodecs.fromCodec(CODEC)
+
+        fun getPokeBall(stack: ItemStack): PokeBall? {
+            return stack.get(CobblemonItemComponents.POKE_BALL)?.name?.let(PokeBalls::getPokeBall)
+        }
+
+        fun setPokeBall(stack: ItemStack, ball: PokeBall): ItemStack {
+            stack.set(CobblemonItemComponents.POKE_BALL, PokeBallComponent(ball.name))
+            return stack
+        }
+
+        fun removePokeBall(stack: ItemStack): ItemStack {
+            stack.remove(CobblemonItemComponents.POKE_BALL)
+            return stack
+        }
+
+        fun createStack(ball: PokeBall?): ItemStack {
+            var result = PokeBalls.STRANGE_BALL
+            if (ball == null) {
+                Cobblemon.LOGGER.error("Attempted to create poke ball stack with null config, falling to strange ball.")
+            } else {
+                result = ball
+            }
+            val stack = ItemStack(CobblemonItems.STRANGE_BALL)
+            return setPokeBall(stack, result)
+        }
+
+    }
+
+    constructor(component: PokeBallComponent): this(component.name)
+
+    val ball: PokeBall
+        get() = PokeBalls.getPokeBall(name)
+}
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt i/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
index 23cd696bea..c2990fbb80 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/item/group/CobblemonItemGroups.kt
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common.item.group
 import com.cobblemon.mod.common.Cobblemon
 import com.cobblemon.mod.common.CobblemonItemComponents
 import com.cobblemon.mod.common.CobblemonItems
+import com.cobblemon.mod.common.api.pokeball.PokeBalls
 import com.cobblemon.mod.common.api.riding.stats.RidingStat
 import com.cobblemon.mod.common.api.tms.TechnicalMachines
 import com.cobblemon.mod.common.block.TypeGemClusterBlock
 import com.cobblemon.mod.common.item.AprijuiceItem
+import com.cobblemon.mod.common.item.components.PokeBallComponent
 import com.cobblemon.mod.common.item.components.RideBoostsComponent
 import com.cobblemon.mod.common.item.components.TMMoveComponent
 import com.cobblemon.mod.common.util.cobblemonResource
@@ -943,6 +945,11 @@ object CobblemonItemGroups {

     private fun utilityItemEntries(displayContext: ItemDisplayParameters, entries: Output) {
         CobblemonItems.pokeBalls.forEach(entries::accept)
+
+        PokeBalls.custom
+            .filter { (_, value) -> CobblemonItems.pokeBalls.none { it.pokeBall.name == value.name } }
+            .forEach { entries.accept(PokeBallComponent.createStack(it.value)) }
+
         CobblemonItems.pokeRods.forEach(entries::accept)
         CobblemonItems.pokedexes.forEach(entries::accept)
     }
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt i/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
index 1644797628..77c9bc35d4 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/pokeball/PokeBall.kt
@@ -34,9 +34,10 @@ import net.minecraft.world.item.Rarity
  * @property waterDragValue The value of the water drag modifier when the entity travels, default is 0.8.
  * @property model2d The identifier for the resource this Pokéball will use for the 2d model.
  * @property model3d The identifier for the resource this Pokéball will use for the 3d model.
+ * @property ancient Marks this Pokéball as ancient, which makes it play different animations on catch hops
  */
-open class PokeBall(
-    val name: ResourceLocation = cobblemonResource("weird_ball"),
+class PokeBall(
+    val name: ResourceLocation = cobblemonResource("strange_ball"),
     val catchRateModifier: CatchRateModifier = CatchRateModifier.DUMMY,
     val effects: List<CaptureEffect> = listOf(),
     val itemProperties: PokeBallProperties = PokeBallProperties(),
@@ -51,6 +52,7 @@ open class PokeBall(
         val rarity: Rarity = Rarity.COMMON
     )

+    @Transient
     val struct = QueryStruct(hashMapOf())
         .addFunction("name") { StringValue(name.toString()) }
         .addFunction("water_drag_value") { DoubleValue(waterDragValue) }
@@ -59,6 +61,7 @@ open class PokeBall(
 //        .addFunction("item") {  } // requires a registry which is hard

     // This gets attached during item registry
+    @Transient
     internal lateinit var stack: ItemStack

     fun item(): PokeBallItem = this.stack.item as PokeBallItem
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CobblemonCaptureEffectAdapter.kt i/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CobblemonCaptureEffectAdapter.kt
new file mode 100644
index 0000000000..c57d7ed2f0
--- /dev/null
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CobblemonCaptureEffectAdapter.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.util.adapters
+
+import com.cobblemon.mod.common.api.pokeball.catching.CaptureEffect
+import com.cobblemon.mod.common.util.readString
+import com.cobblemon.mod.common.util.writeString
+import com.google.common.collect.HashBiMap
+import com.google.gson.Gson
+import com.google.gson.GsonBuilder
+import com.google.gson.JsonDeserializationContext
+import com.google.gson.JsonDeserializer
+import com.google.gson.JsonElement
+import com.google.gson.JsonSerializationContext
+import com.google.gson.JsonSerializer
+import net.minecraft.network.FriendlyByteBuf
+import java.lang.reflect.Type
+import kotlin.reflect.KClass
+
+object CobblemonCaptureEffectAdapter : JsonDeserializer<CaptureEffect>, JsonSerializer<CaptureEffect> {
+    private const val VARIANT = "variant"
+
+    private val types = HashBiMap.create<String, KClass<out CaptureEffect>>()
+
+    val gson: Gson by lazy {
+        GsonBuilder()
+            .registerTypeAdapter(CaptureEffect::class.java, CobblemonCaptureEffectAdapter)
+            .setPrettyPrinting()
+            .create()
+    }
+
+    fun <T : CaptureEffect> registerType(id: String, type: KClass<T>) {
+        this.types[id.lowercase()] = type
+    }
+
+    override fun deserialize(
+        json: JsonElement,
+        typeOfT: Type,
+        context: JsonDeserializationContext,
+    ): CaptureEffect {
+        val variant =
+            json.asJsonObject
+                .get(VARIANT)
+                .asString
+                .lowercase()
+        val type = getModifierType(variant)
+        return context.deserialize(json, type.java)
+    }
+
+    override fun serialize(
+        effect: CaptureEffect,
+        typeOfSrc: Type,
+        context: JsonSerializationContext,
+    ): JsonElement {
+        val json = context.serialize(effect, effect::class.java).asJsonObject
+        val variant = getVariant(effect)
+        json.addProperty(VARIANT, variant)
+        return json
+    }
+
+    fun encode(effect: CaptureEffect, buffer: FriendlyByteBuf) {
+        buffer.writeString(gson.toJson(effect, CaptureEffect::class.java))
+    }
+
+    fun decode(buffer: FriendlyByteBuf): CaptureEffect = gson.fromJson(buffer.readString(), CaptureEffect::class.java)
+
+    private fun getVariant(effect: CaptureEffect): String {
+        val variant =
+            this.types.inverse()[effect::class]
+                ?: throw IllegalArgumentException("Cannot resolve pokeball capture effect for type ${effect::class.qualifiedName}")
+
+        return variant
+    }
+
+    private fun getModifierType(variant: String): KClass<out CaptureEffect> {
+        val effectType =
+            this.types[variant] ?: throw IllegalArgumentException("Cannot resolve pokeball capture effect type for variant $variant")
+
+        return effectType
+    }
+}
diff --git c/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CatchRateModifierAdapter.kt i/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CobblemonCatchRateModifierAdapter.kt
similarity index 94%
rename from common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CatchRateModifierAdapter.kt
rename to common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CobblemonCatchRateModifierAdapter.kt
index 52af19e392..d2199cc29e 100644
--- c/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CatchRateModifierAdapter.kt
+++ i/common/src/main/kotlin/com/cobblemon/mod/common/util/adapters/CobblemonCatchRateModifierAdapter.kt
@@ -30,7 +30,7 @@ import net.minecraft.resources.ResourceLocation
 import java.lang.reflect.Type
 import kotlin.reflect.KClass

-object CatchRateModifierAdapter : CatchRateModifierAdapter {
+object CobblemonCatchRateModifierAdapter : CatchRateModifierAdapter {
     private const val VARIANT = "variant"

     private val types = HashBiMap.create<String, KClass<out CatchRateModifier>>()
@@ -45,7 +45,7 @@ object CatchRateModifierAdapter : CatchRateModifierAdapter {

     val gson: Gson by lazy {
         GsonBuilder()
-            .registerTypeAdapter(CatchRateModifier::class.java, CatchRateModifierAdapter)
+            .registerTypeAdapter(CatchRateModifier::class.java, CobblemonCatchRateModifierAdapter)
             .registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
             .registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
             .registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
@@ -91,14 +91,14 @@ object CatchRateModifierAdapter : CatchRateModifierAdapter {
     private fun getVariant(modifier: CatchRateModifier): String {
         val variant =
             this.types.inverse()[modifier::class]
-                ?: throw IllegalArgumentException("Cannot resolve evolution requirement for type ${modifier::class.qualifiedName}")
+                ?: throw IllegalArgumentException("Cannot resolve pokeball catchrate modifier for type ${modifier::class.qualifiedName}")

         return variant
     }

     private fun getModifierType(variant: String): KClass<out CatchRateModifier> {
         val requirementType =
-            this.types[variant] ?: throw IllegalArgumentException("Cannot resolve evolution requirement type for variant $variant")
+            this.types[variant] ?: throw IllegalArgumentException("Cannot resolve pokeball catchrate modifier type for variant $variant")

         return requirementType
     }
2026-10-11 14:15:14 +03:00
Gitoido f6f9d1329d feat+refactor: introduce CatchRateModifierAdapter and update basic modifiers to support serialization 2026-10-11 14:13:52 +03:00
Gitoido ae2f91a94d refactor: better way to derive root bone of pokeball model 2026-10-11 14:13:52 +03:00
Gitoido 74ff99c478 fix: mixed up npc and pokeball poser directories 2026-10-11 14:13:51 +03:00
Gitoido d6367518c6 refactor: step 2 - lets get rid of hardcoded posers 2026-10-11 14:13:51 +03:00
Gitoido a2aa4bbd7d refactor: step 1 - use itemStack 2026-10-11 14:13:51 +03:00
Polymeta 760f48d850 Merge branch 'evo-tree-fumble' into 'main'
Evo tree now throws a warning instead of crashing when failing to build a mon's evo tree

See merge request cable-mc/cobblemon!2481
2026-10-11 06:41:08 +00:00
lionandPolymeta bf296b4921 Evo tree now throws a warning instead of crashing when failing to build a mon's evo tree 2026-10-11 06:41:08 +00:00
Hiroku 49886389cf Fine the snapshot can be true again 2026-10-11 12:57:43 +10:00
Hiroku 643bf9068b But what if the snapshot was false 2026-10-11 10:54:55 +10:00
Hiroku 53f3258246 Merge remote-tracking branch 'origin/lang-updates' 2026-10-11 10:53:42 +10:00
Hiroku 1e9895ec84 buncha changelog updates [skip-ci] 2026-10-11 10:49:11 +10:00
Polymeta 52dacb03ae Merge branch 'evo-tree-pls-behave' into 'main'
Evo tree small adjustments

See merge request cable-mc/cobblemon!2479
2026-10-10 23:47:53 +00:00
lionandPolymeta 85b0f3f393 Evo tree small adjustments 2026-10-10 23:47:53 +00:00
Polymeta a04a91bf24 Merge branch 'feature/evolution-mania' into 'main'
tweak some evo requirements

See merge request cable-mc/cobblemon!2476
2026-10-10 21:25:13 +00:00
Polymeta 5e814f7ab5 tweak some evo requirements 2026-10-10 21:25:13 +00:00
Polymeta 2780f491d6 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2478
2026-10-10 19:44:59 +00:00
ewwww 2d3ec7aff1 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-10-10 14:51:28 -04:00
ewwww cb7baf2696 changelog 2026-10-10 14:51:12 -04:00
Polymeta 5cc069df78 Merge branch 'implement_pokemon' into 'main'
Dipplin and Hydrapple shiny fixes

See merge request cable-mc/cobblemon!2477
2026-10-10 16:03:26 +00:00
Polymeta 9222476c1f Merge branch 'fix/qa-bundle' into 'main'
fix: an assortment of qa bugs

Closes #2070

See merge request cable-mc/cobblemon!2473
2026-10-10 15:40:51 +00:00
Kenji_64 f220755f99 Dipplin and Hydrapple shiny fixes 2026-10-10 17:31:21 +02:00
Gitoido d55bbf237c chore: move scale changes to other place for now 2026-10-10 18:22:31 +03:00
Watuneni iteiteandWeblate 83a9d44c3a Translated using Weblate (Japanese)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-10 14:22:54 +00:00
Polymeta c5a802eaf6 changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-10 15:04:26 +01:00
Polymeta 2e27875460 Merge branch 'lang-updates' 2026-10-10 15:04:06 +01:00
Polymeta cd66887faa [skip-ci] tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-10 14:11:50 +01:00
Polymeta d665c87c21 more spawn tweaks
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-10 14:02:26 +01:00
Polymeta 044d3102b6 Merge branch 'fix-evo-tree-crash' into 'main'
Fix grotle evolution crashing evo tree and species features not synching to the client

See merge request cable-mc/cobblemon!2456
2026-10-10 12:51:04 +00:00
lionandPolymeta c421a93b06 Fix grotle evolution crashing evo tree and species features not synching to the client 2026-10-10 12:51:03 +00:00
Polymeta 2865aa2272 Merge branch 'implement_pokemon' into 'main'
poser tweaks + jellicent placeholders

See merge request cable-mc/cobblemon!2474
2026-10-10 12:15:04 +00:00
Polymeta 7c4aae3e9d Merge branch '1.8.2-tag-fixes' into 'main'
Fixes to regional tags.

See merge request cable-mc/cobblemon!2475
2026-10-10 12:14:09 +00:00
Veraxiel d9f671f2a8 Fixes to regional tags. 2026-10-10 07:00:14 -04:00
ewwww 69733b530b poser tweaks + jellicent placeholders 2026-10-10 05:44:57 -04:00
Gitoido d4471c30bc fix: use effective scale instead of scale modifier to get a size category for summary/scanner/molang 2026-10-10 09:38:02 +03:00
Gitoido 185f7e03f5 chore: changelog skip-ci 2026-10-10 09:24:24 +03:00
Gitoido a45eb0eb21 fix: close and open pc gui silently via command 2026-10-10 09:18:30 +03:00
Polymeta ed67142379 Merge branch '1.8.2-tag-fixes' into 'main'
Updates to some tags to fix some errors and add future biome support.

See merge request cable-mc/cobblemon!2472
2026-10-10 05:46:36 +00:00
Gitoido 13169c6848 fix: align oricorio pau form translation keys 2026-10-10 08:35:25 +03:00
Veraxiel 8be134ee2f More fixes. 2026-10-09 21:13:26 -04:00
Veraxiel 8c3206a413 Updates to some tags to fix some errors and add future biome support. 2026-10-09 20:03:32 -04:00
Watuneni iteiteandWeblate 4d4c071b95 Translated using Weblate (Japanese)
Currently translated at 100.0% (9472 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-09 23:21:40 +00:00
Polymeta b82c27754f Merge branch 'fix/minior-random-core-color' into 'main'
fix: minior core color should be decided randomly

See merge request cable-mc/cobblemon!2471
2026-10-09 16:57:27 +00:00
Polymeta 63afec184e Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2463
2026-10-09 16:41:24 +00:00
Gitoido 65699f045e fix: minior core color should be decided randomly 2026-10-09 19:38:34 +03:00
JakotensandJakotens 90dc489547 Minor poser fixes for Mime Jr. and slight model adjustments to Mime Jr. and Corsola (Johtonian form only).
Signed-off-by: Jakotens <jjakobdupuyy@gmail.com>
2026-10-08 13:47:42 -06:00
ewwww b2142fed1d frog tweaks 2026-10-08 03:07:03 -04:00
ewwww fef21516e9 lang 2026-10-08 03:05:45 -04:00
ewwww 95d43de79f various minior tweaks 2026-10-08 02:16:38 -04:00
ewwww 92bccf620f Merge branch 'main' into implement_pokemon 2026-10-07 15:40:14 -04:00
ewwww 15f8a9ee71 Merge branch 'main' into implement_pokemon 2026-10-07 15:39:42 -04:00
ewwww e5750cbba0 lang fixes for groudon, kyogre, and oricorio 2026-10-07 12:58:20 -04:00
ewwww c6482c82cd various minior tweaks 2026-10-07 12:57:06 -04:00
Kenji_64 c470894bd3 Fixed Politoed's hitbox 2026-10-07 15:29:52 +02:00
Kenji_64 a940e32f8d Fixed a texture error with Shiny Greninja 2026-10-07 15:29:21 +02:00
ewwww b6b59eccad vivillon dex tweak 2026-10-07 06:52:25 -04:00
ewwww b520b94fae minior lang fixes + tweaks to form & dex data 2026-10-07 06:52:16 -04:00
YasitoandWeblate 1969af65b9 Translated using Weblate (French (Canada))
Currently translated at 95.1% (9016 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/fr_CA/
2026-09-30 09:25:23 +00:00
SultanandWeblate 905d63e098 Translated using Weblate (Russian)
Currently translated at 85.6% (8110 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-21 16:38:11 +00:00
Nico MerenqandWeblate 1ea8ab7dcc Translated using Weblate (Russian)
Currently translated at 85.6% (8110 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-21 16:38:11 +00:00
geumuandWeblate a45d21b279 Translated using Weblate (Korean)
Currently translated at 99.6% (9436 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-09-21 16:38:10 +00:00
geumuandWeblate efe8f34413 Translated using Weblate (Korean)
Currently translated at 99.6% (9436 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-09-20 13:11:32 +00:00
Nico MerenqandWeblate 6bb23582c4 Translated using Weblate (Russian)
Currently translated at 85.5% (8102 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-19 04:42:37 +00:00
SultanandWeblate 56ccb43997 Translated using Weblate (Russian)
Currently translated at 85.5% (8102 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-09-19 04:42:36 +00:00
Dmytro BunnuaandWeblate 5fbbcb5707 Translated using Weblate (Ukrainian)
Currently translated at 75.9% (7194 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/uk/
2026-09-15 09:37:39 +00:00
Gustavo PuerariandWeblate 580a24eac3 Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.8% (9175 of 9472 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-09-13 13:18:28 +00:00
166 changed files with 5631 additions and 2424 deletions
+6 -169
View File
@@ -1,200 +1,37 @@
# Changelog
## [1.8.2 (MONTH xth, 2026)](#1-8-2)
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
### Additions
- The following items can now be placed on the ground as blocks:
- King's Rock, Galarica Cuff, Unremarkable Teacup, Masterpiece Teacup, Chipped Pot, Cracked Pot, Sachet, Metal Powder, Quick Powder, Bright Powder, Soft Sand, Electirizer, Magmarizer, Icy Rock, Heat Rock, Smooth Rock, Damp Rock, Destiny Knot, Luminous Moss, Never-Melt Ice, Wide Lens, Scope Lens, and Zoom Lens.
- Iron Ball, Metal Coat, Throat Spray, Terrain Extender, Medicinal Brew, Moomoo Milk, HP Up, Protein, Iron, Carbos, Calcium, Zinc, PP Up, PP Max, Upgrade, Dubious Disc, and Loaded Dice.
- The following items can now be placed on various surfaces:
- Galarica Wreath, Magnet, Flame Orb, Life Orb, Toxic Orb, Light Ball, and Smoke Ball.
- The following items can now be placed as blocks and have additional functionality:
- Ring Target, Metronome, and Eject Button.
- The following items can now be worn by Players and Pokémon:
- Galarica Wreath, Cleanse Tag, Spell Tag, Sharp Beak, Covert Cloak, Shell Helmet, and Never-Melt Ice.
- Wearable items can now be equipped by a player by holding it and right-clicking (or rebound use/interact)
- Wearable items that can be placed down are exempt from this behaviour.
- Mint Leaves can now be placed on the ground as Leaf Litter, or in Flower Pots
- Added Oricorio Flowers
- Added Terrain Plants
- Berries can now be placed in Flower Pots
- The evolution tree can now be viewed on the Pokédex, showing a Pokémon's full family line and what's needed to evolve into each stage.
- Added custom sounds to a bunch of items that can be placed as blocks: liquid bottles, pill bottles, powders and dice.
- New Cosmetic Item for Pikachu: Paper
- Burmy changes its cloak after a battle it took part in, based on the biome: Trash Cloak in the Deep Dark, Nether and End, Plant Cloak in lush biomes such as forests, jungles, plains and swamps, and Sandy Cloak in sandy biomes, beaches and caves.
### Fixes
- Medicinal Leeks can now be eaten when they aren't being planted, matching their food properties.
- Sweeping attacks no longer set owned Pokémon on fire (e.g. with Fire Aspect) or knock them back.
- Fixed #updateMovesOnFormChange not properly initializing a form-swapped Pokemon's non-form_change moves.
### TEST
### Pokémon Added
#### Generation 4
- Snover
- Abomasnow
- Burmy
- Wormadam
- Mothim
#### Generation 5
- Tympole
- Palpitoad
- Seismitoad
#### Generation 6
- Helioptile
- Heliolisk
#### Generation 7
- Grubbin
- Charjabug
- Vikavolt
- Minior
#### Generation 8
- Eternatus
#### Generation 9
- Greavard
- Houndstone
### Model updates for the following Pokémon
- Sudowoodo
- Spheal
- Cacnea
- Cacturne
- Binacle
- Barbaracle
- Glimmet
- Glimmora
- Deerling
- Sawsbuck (All forms)
- Absol
- Poliwag
- Politoed
- Ekans
- Oddish
- Gloom
- Vileplume
- Bellossom
- Shroomish
- Chespin
- Quilladin
- Chesnaught
- Fennekin
- Braixen
- Delphox
- Froakie
- Frogadier
- Greninja
- Shinx
- Luxio
- Luxray
- Slakoth
- Riolu
- Lucario
### Animation updates for the following Pokémon
- Treecko (New Shoulder Mount!)
- Bonsly (New Shoulder Mount!)
- Sudowoodo
- Cacnea
- Cacturne
- Ekans
- Arbok
- Oddish
- Gloom
- Vileplume
- Bellossom
- Pichu
- Pikachu
- Raichu (+ Alolan)
- Greninja
- Slakoth
### Changes
- Terrain Seeds can now be planted
- Terrain Seeds are now consumed during PvP and NPC battles
- Glow Item Frames can be crafted using Bright Powder as an alternative to Glow Ink Sacs
- Bright Powder can be used on Signs and Hanging Signs to make text glow, as an alternative to Glow Ink Sacs
- Updated various recipes to display in Recipe Groups within the Recipe Book
- Updated most recipes to display more logically/consistently within the Recipe Book's categories
- Updated the following recipes
- Never-Melt Ice, Focus Sash, Blunder Policy, Weakness Policy, Blank TM
- Make ride rotations less jittery in multiplayer.
- Changes Disc Shelf TMs to use textures instead of tinting and overlays to line up with item display
- Blank TM can now be inserted into the Disc Shelf
- Added TM creative tab
- Recall animation now renders ball in hand with the player arm raised.
- Made regional and Vivillon split-evo locations now mutually exclusive
- Item evolutions now unlock **all** locked evolutions whose requirements are met instead of stopping after the first.
- Exceptions being "forced" evolutions which will continue to stop after the first one
- Removed Mega Drain as a TM
- Vivillon's patterns are now viewable as forms with form names and unique Pokedex entries displayed.
- Slowed the speed of Type Gem growth, and revamped their generation to grow in a more crystal-like shape
- PokeSnacks (and other area spawns) will now start the vertical offset checking from the center rather than base coordinate
- This will help PokeSnacks to work inside caves or tighter spaces better
### Fixes
- Correctly grants "They're Minerals!" advancement when planting a type gem
- Shoulder mounted Pokémon now follow player body animations, improving their positioning with custom player animations and default actions.
- Shoulder mounted Pokémon now sit slightly higher with chest armor equipped for better alignment, with a configurable blacklist.
- Type Gem Blocks now have advancements to give you the recipe in the recipe book (the data for the advancements were inside the recipes somehow??)
- Some other recipes without advancements have also been corrected
- Corrected the TM Recipes for Charge Beam and Payday
- Type Gem blocks now properly generate clusters off of the block.
- Starter Screen Categories will no longer stop rendering if they are bigger than the viewport, and you are not viewing top or bottom edges
- Random Starter option now properly handled in height calculation, so now it won't run off into other categories or be un-accessible due to scroll limits if it causes a new row
- Mochi and Feathers no longer drop glass bottles on use
- Fixed a rare set of concurrency bugs related to reloading packs on NeoForge; fixes a ModernFix issue
- Removed unnecessary conditions for Mimikyu's standing pose. Hopefully fixes the rare instance of it using its shoulder animations during battle in multiplayer scenarios.
- Fixed Rockruff line not blinking
- Fixed Flygon sometimes using its running animation instead of walking
- Fixed Bramblin always sleeping outside of battle
- Fixed Dragonair using its battle idle as a normal idle
- Fixed Lycanroc's head movement
- Added head movement to Archaludon
- Fixed Duraludon line not having any sleep animations
- Fixed Diglett line not having their faint animations
- Fixed Kingambit moving its legs while walking
- Fixed Alolan Diglett's cry not playing
- Fixed shiny alpha Snorlax using the non-shiny texture
- Various Structure fixes
- Fixed shiny Pokémon under certain circumstances forgetting taught TMs
- Fixed certain Pokémon sounds sometimes being doubled when played at specific times in their animations
- Fixed ball capture beam origin coming from behind the ball.
- Fixed Safari Ball catch rate outside of battle
- Fixed alpha eye (and other) particles not respecting client particle density setting in video options
- Fixed TM-Machine UI not closing when player is moving away from it
- Fixed config category for alpha/shiny chance config fields for fossil machine and honey log slathering to Spawning
- Fixed crashes for addon species with non-Cobblemon namespaces, in evolution, fishing bait and Pokédex lookups.
- Bees now grow Apricorns and the Galarica Nut Bush, which were in the bee growables tag but never handled, and correctly spend their nectar when they do
- Saccharine Leaves can now be bone mealed
- Fixed equipping a stack of items instead of one item of a stack
- Fixed TM stack consumption in TMM screen when player teaches a move to species
- Fixed every Miltank giving mushroom stew, not only mushroom variants
- Fixed datapacks with item interaction evolutions that don't specify an item crashing the world on load.
- Fixed the Pokédex evolution tree crashing when a datapack's dex entry is missing an evolution's form.
- Fixed the Pokédex evolution tree crashing with a stack overflow on evolutions that loop back on themselves.
- Fixed form-changing evolutions sometimes resolving to the source form in the Pokédex evolution tree.
- Broken datapack evolutions are now logged and skipped in the Pokédex evolution tree instead of crashing.
### Developer
- Evolution requirements can now set `pokedexIcon` and `pokedexTooltip` directly in their JSON to control how they're shown on the Pokédex's evolution tree, for example:
```json
{
"variant": "attack_defence_ratio",
"pokedexIcon": "minecraft:diamond_sword",
"pokedexTooltip": "cobblemon.ui.pokedex.requirement.attack_defence_ratio.tooltip"
}
```
- Fixed wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing pokemon to appear without entity link
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon
### Molang & Datapacks
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
- Added Molang functions for individual Pokédex ids, wherein a subdex is passed in as a string to `dex_caught_count` etc.
- Added a `battle_ended` callback, backed by the new `BATTLE_ENDED` event, which fires whenever a battle ends for any reason, alongside a multitude of flags not previously covered by `BATTLE_VICTORY`.
- Added `battle_pokemon` to battle actor structs and `has_faced_opponents` to battle Pokémon structs.
- Added the `cobblemon:form_change/bagworm_cloak/plant_cloak`, `sandy_cloak` and `trash_cloak` biome tags, which control Burmy's cloak after battle.
### Particles
#### New Move Animations
### Localization
- Updated translations for:
+4 -1
View File
@@ -36,6 +36,7 @@
- Added `cobblemon:catch_alpha_pokemon` advancement trigger and `totalAlphaCaptureCount` stat for captured Alpha Pokémon.
### New rideable Pokémon
- Pidgeot
- Crobat
- Skarmory
- Milotic
@@ -106,7 +107,9 @@
- Avalugg (Hisuian Form)
### Model updates for the following Pokémon
- Pidgey
- Pidgeotto
- Pidgeot
- Sandshrew
- Sandslash
- Nidoking
+211
View File
@@ -0,0 +1,211 @@
# Changelog
## [1.8.2 (October 11th, 2026)](#1-8-2)
### Additions
- The following items can now be placed on the ground as blocks:
- King's Rock, Galarica Cuff, Unremarkable Teacup, Masterpiece Teacup, Chipped Pot, Cracked Pot, Sachet, Metal Powder, Quick Powder, Bright Powder, Soft Sand, Electirizer, Magmarizer, Icy Rock, Heat Rock, Smooth Rock, Damp Rock, Destiny Knot, Luminous Moss, Never-Melt Ice, Wide Lens, Scope Lens, and Zoom Lens.
- I wasn't finished. Iron Ball, Metal Coat, Throat Spray, Terrain Extender, Medicinal Brew, Moomoo Milk, HP Up, Protein, Iron, Carbos, Calcium, Zinc, PP Up, PP Max, Upgrade, Dubious Disc, and Loaded Dice.
- The following items can now be placed on various surfaces:
- Galarica Wreath, Magnet, Flame Orb, Life Orb, Toxic Orb, Light Ball, and Smoke Ball.
- The following items can now be placed as blocks and have additional functionality:
- Ring Target, Metronome, and Eject Button.
- The following items can now be worn by Players and Pokémon:
- Galarica Wreath, Cleanse Tag, Spell Tag, Sharp Beak, Covert Cloak, Shell Helmet, and Never-Melt Ice.
- Wearable items can now be equipped by a player by holding it and right-clicking (or whatever you've rebound use/interact to).
- Wearable items that can be placed down are exempt from this behaviour.
- Mint Leaves can now be placed on the ground as Leaf Litter, or in Flower Pots.
- The recall animation now renders the ball in hand with the player's arm raised.
- Added Oricorio Flowers.
- Added Terrain Plants, four plants grown from Electric, Grassy, Misty, and Psychic Seeds, which glow under specific environmental conditions and can be farmed for more of their seeds.
- Berries can now be placed in Flower Pots.
- The evolution tree for a Pokémon can now be viewed in the Pokédex, showing the Pokémon's full family line and what's needed to evolve into each stage.
- Added custom sounds to a bunch of items that can be placed as blocks: liquid bottles, pill bottles, powders, and dice.
- Added paper as a new cosmetic item for Pikachu.
- Burmy changes its cloak after a battle it took part in, based on the biome.
- Trash Cloak in the Deep Dark, Nether and End.
- Plant Cloak in lush biomes such as forests, jungles, plains and swamps.
- Sandy Cloak in sandy biomes, beaches and caves.
### Pokémon Added
#### Generation 4
- Snover
- Abomasnow
- Burmy
- Wormadam
- Mothim
#### Generation 5
- Tympole
- Palpitoad
- Seismitoad
#### Generation 6
- Helioptile
- Heliolisk
#### Generation 7
- Grubbin
- Charjabug
- Vikavolt
- Minior
#### Generation 8
- Eternatus
#### Generation 9
- Greavard
- Houndstone
### Model updates for the following Pokémon
- Sudowoodo
- Spheal
- Cacnea
- Cacturne
- Binacle
- Barbaracle
- Glimmet
- Glimmora
- Deerling
- Sawsbuck (All forms)
- Absol
- Poliwag
- Politoed
- Ekans
- Oddish
- Gloom
- Vileplume
- Bellossom
- Shroomish
- Chespin
- Quilladin
- Chesnaught
- Fennekin
- Braixen
- Delphox
- Froakie
- Frogadier
- Greninja
- Shinx
- Luxio
- Luxray
- Slakoth
- Riolu
- Lucario
- Corsola
- Mime Jr.
### Animation updates for the following Pokémon
- Treecko (New Shoulder Mount!)
- Bonsly (New Shoulder Mount!)
- Sudowoodo
- Cacnea
- Cacturne
- Ekans
- Arbok
- Oddish
- Gloom
- Vileplume
- Bellossom
- Pichu
- Pikachu
- Raichu (+ Alolan)
- Greninja
- Slakoth
### Changes
- Slowed the speed of Type Gem growth, and revamped their generation to grow in a more crystal-like shape.
- Terrain Seeds can now be planted.
- Terrain Seeds are now consumed during PvP and NPC battles.
- Glow Item Frames can be crafted using Bright Powder as an alternative to Glow Ink Sacs.
- Bright Powder can be used on Signs and Hanging Signs to make text glow, as an alternative to Glow Ink Sacs.
- Updated various recipes to display in Recipe Groups within the Recipe Book.
- Updated most recipes to display more logically and consistently within the Recipe Book's categories.
- Updated the following recipes
- Never-Melt Ice, Focus Sash, Blunder Policy, Weakness Policy, Blank TM.
- Made ride rotations less jittery in multiplayer.
- Changes Disc Shelf TMs to use textures instead of tinting and overlays to line up with item display.
- Blank TM can now be inserted into the Disc Shelf.
- Added a TM creative tab.
- Made regional and Vivillon split-evo locations mutually exclusive.
- Vivillon's patterns are now viewable as forms with form names and unique Pokédex entries displayed.
- Item evolutions now unlock **all** locked evolutions whose requirements are met instead of stopping after the first.
- Exceptions being "forced" evolutions which stop after the first one.
- Removed Mega Drain as a TM.
- PokeSnacks (and other area spawns) will now start the vertical offset checking from the center rather than base coordinate
- This will help PokeSnacks to work inside caves or tighter spaces better.
### Fixes
- Type Gem blocks now properly generate clusters off of the block.
- Correctly grant "They're Minerals!" advancement when planting a Type Gem.
- Shoulder mounted Pokémon now follow player body animations, improving their positioning with custom player animations and default actions.
- Shoulder mounted Pokémon now sit slightly higher with chest armor equipped for better alignment, with a configurable blacklist.
- Type Gem Blocks now have advancements to give you the recipe in the recipe book (the data for the advancements were inside the recipes somehow??).
- Some other recipes without advancements have also been corrected.
- Corrected the TM Recipes for Charge Beam and Payday.
- Starter Screen Categories will no longer stop rendering if they are bigger than the viewport and you are not viewing the top or bottom edges.
- The Random Starter option no longer runs off into other categories or becomes unaccessible due to scroll limits if it causes a new row.
- Mochi and Feathers no longer drop glass bottles on use.
- Fixed a rare set of concurrency bugs related to reloading packs on NeoForge; fixes a ModernFix compatibility issue.
- Removed unnecessary conditions for Mimikyu's standing pose. Hopefully fixes the rare instance of it using its shoulder animations during battle in multiplayer scenarios.
- Fixed Rockruff line not blinking.
- Fixed Flygon sometimes using its running animation instead of walking.
- Fixed Bramblin always sleeping outside of battle.
- Fixed Dragonair using its battle idle as a normal idle.
- Fixed Lycanroc's head movement.
- Added head movement to Archaludon.
- Fixed Duraludon line not having any sleep animations.
- Fixed Diglett line not having their faint animations.
- Fixed Kingambit moving its legs while walking.
- Fixed Alolan Diglett's cry not playing.
- Fixed shiny alpha Snorlax using the non-shiny texture.
- Fixed Crabominable's head breaking when looking around.
- Fixed Shiny Dipplin and Hydrapple's textures.
- Various structure fixes.
- Fixed shiny Pokémon under certain circumstances forgetting taught TMs.
- Fixed certain Pokémon sounds sometimes being doubled when played at specific times in their animations.
- Fixed ball capture beam origin coming from behind the ball.
- Fixed Safari Ball catch rate outside of battle.
- Fixed alpha eye (and other) particles not respecting the client particle density setting in video options.
- Fixed the TM Machine UI not closing when player is moving away from it.
- Moved the config category for alpha/shiny chance config fields for fossil machine and honey log slathering to Spawning.
- Fixed crashes for addon species with non-Cobblemon namespaces in evolution, fishing bait, and Pokédex lookups.
- Bees will now grow Apricorns and the Galarica Nut Bush, which were in the bee growables tag but never handled, and correctly spend their nectar when they do.
- Saccharine Leaves can now be bone mealed.
- Fixed equipping a stack of wearable items instead of one item of a stack.
- Fixed TM stack consumption in TM Machine screen when a player teaches a move to a Pokémon.
- Fixed every Miltank giving mushroom stew, not only mushroom variants.
- Medicinal Leeks can now be eaten when they aren't being planted, matching their food properties.
- Sweeping attacks no longer set owned Pokémon on fire (e.g. with Fire Aspect) or knock them back. Friendly Fire is now off.
- Fixed Pokémon not losing their previous form's signature moves when changing forms (e.g. Rotom keeping Overheat after leaving its Heat form).
- Form-specific moves gained from a form change are now only granted if the Pokémon is high enough level to learn them, and no longer create duplicates in benched moves.
- Spinda now learns Teeter Dance at level 32 instead of via TM.
- Fixed Oricorio Pa'u form description in the Pokédex.
- Fixed PC playing sound on close when opened via command.
### Developer
- Evolution requirements can now set `pokedexIcon` and `pokedexTooltip` directly in their JSON to control how they're shown on the Pokédex's evolution tree, for example:
```json
{
"variant": "attack_defence_ratio",
"pokedexIcon": "minecraft:diamond_sword",
"pokedexTooltip": "cobblemon.ui.pokedex.requirement.attack_defence_ratio.tooltip"
}
```
- Fixed the wrong order of operations in the code flow prior to the `FOSSIL_REVIVED` event causing Pokémon to appear without the entity link.
- Fixed TestPartySlot command not correctly comparing the provided spec with the target Pokémon.
### Molang & Datapacks
- A new `negate` variant for evolution requirements has been added, which will negate (invert) the provided inner condition.
- Added Molang functions for individual Pokédex ids, wherein a subdex is passed in as a string to `dex_caught_count` etc.
- Added a `battle_ended` callback, backed by the new `BATTLE_ENDED` event, which fires whenever a battle ends for any reason, alongside a multitude of flags not previously covered by `BATTLE_VICTORY`.
- Added `battle_pokemon` to battle actor structs and `has_faced_opponents` to battle Pokémon structs.
- Added the `cobblemon:form_change/bagworm_cloak/plant_cloak`, `sandy_cloak` and `trash_cloak` biome tags, which control Burmy's cloak after battle.
### Localization
- Updated translations for:
- Brazilian Portuguese
- Canadian French
- Japanese
- Korean
- Russian
- Ukrainian
@@ -15,7 +15,9 @@ import com.cobblemon.mod.common.ModAPI;
import com.cobblemon.mod.common.item.PokeBallItem;
import com.cobblemon.mod.common.item.PokedexItem;
import com.cobblemon.mod.common.item.WearableItem;
import com.cobblemon.mod.common.item.components.PokeBallComponent;
import com.cobblemon.mod.common.item.components.TMMoveComponent;
import com.cobblemon.mod.common.pokeball.PokeBall;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.ItemModelShaper;
@@ -57,11 +59,27 @@ public abstract class ItemRendererMixin {
cancellable = true
)
private void cobblemon$overrideItemModel(ItemStack stack, ItemDisplayContext renderMode, boolean leftHanded, PoseStack matrices, MultiBufferSource multiBufferSource, int light, int overlay, BakedModel model, CallbackInfo ci) {
boolean shouldBe2d = renderMode == ItemDisplayContext.GUI || renderMode == ItemDisplayContext.FIXED || stack.getItem() == CobblemonItems.TECHNICAL_MACHINE;
boolean shouldBe2d =
renderMode == ItemDisplayContext.GUI
|| renderMode == ItemDisplayContext.FIXED
|| stack.getItem() == CobblemonItems.TECHNICAL_MACHINE;
boolean shouldBe3d =
renderMode == ItemDisplayContext.FIRST_PERSON_LEFT_HAND
|| renderMode == ItemDisplayContext.FIRST_PERSON_RIGHT_HAND
|| renderMode == ItemDisplayContext.THIRD_PERSON_LEFT_HAND
|| renderMode == ItemDisplayContext.THIRD_PERSON_RIGHT_HAND
|| renderMode == ItemDisplayContext.GROUND
|| renderMode == ItemDisplayContext.HEAD;
ResourceLocation resourceLocation = null;
if (stack.getItem() instanceof PokeBallItem) {
if (shouldBe2d) resourceLocation = PokeBallComponent.getPokeBall(stack).getModel2d();
if (shouldBe3d) resourceLocation = PokeBallComponent.getPokeBall(stack).getModel3d();
}
if (shouldBe2d) {
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel2d();
else if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
else if (stack.getItem() == CobblemonItems.TECHNICAL_MACHINE) {
TMMoveComponent component = stack.get(CobblemonItemComponents.TM_MOVE);
if (component != null) {
@@ -83,16 +101,21 @@ public abstract class ItemRendererMixin {
@Inject(method = "getModel", at = @At("HEAD"), cancellable = true)
private void cobblemon$getItemModel(ItemStack stack, Level world, LivingEntity entity, int seed, CallbackInfoReturnable<BakedModel> cir) {
ResourceLocation resourceLocation = null;
if (stack.getItem() instanceof PokeBallItem pokeBallItem) resourceLocation = pokeBallItem.getPokeBall().getModel3d();
else if (stack.getItem() instanceof PokedexItem pokedexItem) {
boolean isScanModel = entity instanceof Player && entity.getUseItem() == stack && entity.isUsingItem();
boolean canOpenScreen = entity != null && ((entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof PokedexItem)) || entity.getMainHandItem() == stack);
resourceLocation = pokedexItem.getType().getItemModelPath(isScanModel ? "scanning" : (canOpenScreen ? null : "off"));
switch (stack.getItem()) {
case PokeBallItem ignored -> resourceLocation = PokeBallComponent.getPokeBall(stack).getModel3d();
case PokedexItem pokedexItem -> {
boolean isScanModel = entity instanceof Player && entity.getUseItem() == stack && entity.isUsingItem();
boolean canOpenScreen = entity != null && ((entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof PokedexItem)) || entity.getMainHandItem() == stack);
resourceLocation = pokedexItem.getType().getItemModelPath(isScanModel ? "scanning" : (canOpenScreen ? null : "off"));
}
case WearableItem wearableItem -> resourceLocation = wearableItem.getModel3d();
default -> {
}
}
else if (stack.getItem() instanceof WearableItem wearableItem) resourceLocation = wearableItem.getModel3d();
if (resourceLocation != null) {
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, MODEL_PATH));
String modelPath = (stack.getItem() instanceof PokeBallItem && stack.has(CobblemonItemComponents.POKE_BALL)) ? "inventory" : MODEL_PATH;
BakedModel model = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, modelPath));
ClientLevel clientWorld = world instanceof ClientLevel ? (ClientLevel) world : null;
BakedModel overriddenModel = model.getOverrides().resolve(model, stack, clientWorld, entity, seed);
cir.setReturnValue(overriddenModel == null ? this.itemModelShaper.getModelManager().getMissingModel() : overriddenModel);
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.mixin.client;
import com.cobblemon.mod.common.client.CobblemonBakingOverrides;
import com.cobblemon.mod.common.client.CobblemonClient;
import net.minecraft.client.Minecraft;
import net.minecraft.client.color.block.BlockColors;
import net.minecraft.client.renderer.block.model.BlockModel;
@@ -19,6 +20,7 @@ import net.minecraft.client.resources.model.UnbakedModel;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.Resource;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.ItemDisplayContext;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
@@ -29,6 +31,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@Mixin(ModelBakery.class)
public abstract class ModelLoaderMixin {
@@ -38,8 +41,10 @@ public abstract class ModelLoaderMixin {
@Shadow protected abstract void loadItemModelAndDependencies(ResourceLocation resourceLocation);
@Shadow protected abstract void loadSpecialItemModelAndDependencies(ModelResourceLocation modelResourceLocation);
@Unique
List<String> dynamicFolders = Arrays.asList("egg", "tm");
List<String> dynamicFolders = Arrays.asList("egg", "tm", "poke_ball");
@Inject(
method = "<init>",
@@ -67,6 +72,9 @@ public abstract class ModelLoaderMixin {
* hacky at the init stage and hacky at the render stage. Pretty cool in the gameplay stage though.
*
* - Hiro
*
* We also want to load different contexts (first/third person etc.) for poke ball models, so additional check was added for them.
* - Gito
*/
dynamicFolders.forEach(folder -> {
@@ -74,6 +82,15 @@ public abstract class ModelLoaderMixin {
map.forEach((key, value) -> {
ResourceLocation resourceLocation = new ResourceLocation(key.getNamespace(), key.getPath().replace("models/item/", "").replace(".json", ""));
this.loadItemModelAndDependencies(resourceLocation);
if (folder.equals("poke_ball")) {
// also load variants for different display ctx
Arrays.stream(ItemDisplayContext.values())
.forEach(item -> {
ModelResourceLocation model = new ModelResourceLocation(resourceLocation, item.getSerializedName());
this.loadSpecialItemModelAndDependencies(model);
});
}
});
});
}
@@ -47,7 +47,7 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, ResourceKey
@JvmField
val EMPTY_POKEBALL: EntityType<EmptyPokeBallEntity> = this.create(
EMPTY_POKEBALL_KEY.path,
EntityType.Builder.of({ _, world -> EmptyPokeBallEntity(PokeBalls.POKE_BALL, world) }, MobCategory.MISC)
EntityType.Builder.of({ _, world -> EmptyPokeBallEntity(PokeBalls.STRANGE_BALL, world) }, MobCategory.MISC)
.build(EMPTY_POKEBALL_KEY.toString())
)
@@ -99,4 +99,4 @@ object CobblemonEntities : PlatformRegistry<Registry<EntityType<*>>, ResourceKey
consumer(POKEMON, PokemonEntity.createAttributes())
consumer(NPC, NPCEntity.createAttributes())
}
}
}
@@ -99,6 +99,12 @@ object CobblemonItemComponents : PlatformRegistry<Registry<DataComponentType<*>>
.networkSynchronized(ByteBufCodecs.BOOL)
.build())
@JvmField
val POKE_BALL: DataComponentType<PokeBallComponent> = create("poke_ball", DataComponentType.builder<PokeBallComponent>()
.persistent(PokeBallComponent.CODEC)
.networkSynchronized(PokeBallComponent.PACKET_CODEC)
.build())
fun register() {
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:pokemon_item"), POKEMON_ITEM)
Registry.register(BuiltInRegistries.DATA_COMPONENT_TYPE, ResourceLocation.parse("cobblemon:bait"), BAIT)
@@ -112,9 +118,10 @@ 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:poke_ball"), POKE_BALL)
}
override val registry = BuiltInRegistries.DATA_COMPONENT_TYPE
override val resourceKey = Registries.DATA_COMPONENT_TYPE
}
}
@@ -172,6 +172,8 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val ANCIENT_GIGATON_BALL = pokeBallItem(PokeBalls.ANCIENT_GIGATON_BALL)
@JvmField
val ANCIENT_ORIGIN_BALL = pokeBallItem(PokeBalls.ANCIENT_ORIGIN_BALL)
@JvmField
val STRANGE_BALL = pokeBallItem(PokeBalls.STRANGE_BALL)
val pokedexes = mutableListOf<PokedexItem>()
@JvmField
@@ -1591,7 +1593,7 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun pokeBallItem(pokeBall: PokeBall): PokeBallItem {
val item = create(pokeBall.name.path, PokeBallItem(pokeBall))
pokeBall.item = item
pokeBall.stack = item.defaultInstance
pokeBalls.add(item)
return item
}
@@ -425,7 +425,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MovesRegistrySyncPacket.ID, MovesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(BerryRegistrySyncPacket.ID, BerryRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesRegistrySyncPacket.ID, SpeciesRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(SpeciesEvolutionSyncPacket.ID, SpeciesEvolutionSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(DexEvolutionDisplaySyncPacket.ID, DexEvolutionDisplaySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PropertiesCompletionRegistrySyncPacket.ID, PropertiesCompletionRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(StandardSpeciesFeatureSyncPacket.ID, StandardSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(GlobalSpeciesFeatureSyncPacket.ID, GlobalSpeciesFeatureSyncPacket::decode, DataRegistrySyncPacketHandler()))
@@ -11,9 +11,6 @@ package com.cobblemon.mod.common.api.conditional
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -24,19 +21,11 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 17th, 2022
*/
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>>, JsonSerializer<RegistryLikeCondition<B>> {
interface RegistryLikeAdapter<B> : JsonDeserializer<RegistryLikeCondition<B>> {
val registryLikeConditions: MutableList<(JsonElement) -> RegistryLikeCondition<B>?>
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): RegistryLikeCondition<B> {
return registryLikeConditions.firstNotNullOfOrNull { it(json) }
?: throw IllegalArgumentException("Unable to deserialize $json")
}
override fun serialize(src: RegistryLikeCondition<B>, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
return when (src) {
is RegistryLikeTagCondition<B> -> JsonPrimitive(RegistryLikeTagCondition.PREFIX + src.tag.location())
is RegistryLikeIdentifierCondition<B> -> JsonPrimitive(src.identifier.toString())
else -> throw IllegalArgumentException("Unable to serialize $src")
}
}
}
@@ -25,6 +25,7 @@ data class PokemonCapturedEvent (
"pokemon" to pokemon.struct,
"player" to player.asMoLangValue(),
"poke_ball" to pokeBallEntity.struct,
"item" to player.level().itemRegistry.wrapAsHolder(pokeBallEntity.pokeBall.item).asMoLangValue(Registries.ITEM)
"item" to player.level().itemRegistry.wrapAsHolder(pokeBallEntity.pokeBall.item()).asMoLangValue(Registries.ITEM),
"stack" to pokeBallEntity.pokeBall.stack.asMoLangValue(player.level().registryAccess())
)
}
}
@@ -37,6 +37,7 @@ import com.cobblemon.mod.common.api.molang.function.NPCMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PCMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PartyMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PlayerMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PokeBallMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PokedexMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PokemonEntityMoLangFunctions
import com.cobblemon.mod.common.api.molang.function.PokemonMoLangFunctions
@@ -56,6 +57,7 @@ import com.cobblemon.mod.common.api.storage.party.PartyStore
import com.cobblemon.mod.common.api.storage.pc.PCStore
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.effectiveName
@@ -262,6 +264,18 @@ object MoLangFunctions {
return value
}
fun PokeBall.asMoLangValue(): ObjectValue<PokeBall> {
val value =
ObjectValue(
obj = this,
stringify = { it.name.toString() }
)
value.addFunctions(PokeBallMoLangFunctions.attach(this))
return value
}
fun Vec3.asMoLangValue(): ObjectValue<Vec3> {
val value =
ObjectValue(
@@ -389,6 +403,12 @@ object MoLangFunctions {
return this
}
fun QueryStruct.addPokeBallFunctions(ball: PokeBall): QueryStruct {
val addedFunctions = PokeBallMoLangFunctions.attach(ball)
addFunctions(addedFunctions)
return this
}
fun <T : QueryStruct> T.addFunctions(functions: Map<String, Function<MoParams, Any>>): T {
this.functions.putAll(functions)
return this
@@ -23,6 +23,7 @@ import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.api.text.text
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.net.messages.client.effect.SpawnSnowstormEntityParticlePacket
import com.cobblemon.mod.common.util.asArrayValue
@@ -307,6 +308,15 @@ object EntityMoLangFunctions : AbstractMoLangFunctionHolder<Entity>() {
functions["face"] = { params ->
ObjectValue(ReferenceDialogueFaceProvider(entity.id, params.getBooleanOrNull(0) != false))
}
// EmptyPokeBallEntity is a subclass of PosableEntity
// meaning we need to add a condition here to attach our func
// only to instance of EmptyPokeBallEntity
if (entity is EmptyPokeBallEntity) {
functions["capture_state"] = {
StringValue(entity.captureState.toString())
}
}
}
is Schedulable -> {
@@ -0,0 +1,25 @@
/*
* 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.molang.function
import com.bedrockk.molang.runtime.MoParams
import com.bedrockk.molang.runtime.value.DoubleValue
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.getStringOrNull
object PokeBallMoLangFunctions : AbstractMoLangFunctionHolder<PokeBall>() {
override fun PokeBall.moLangFunctions(): MutableMap<String, (MoParams) -> Any> {
val ball = this
val functions = hashMapOf<String, (MoParams) -> Any>()
return functions
}
}
@@ -47,6 +47,10 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
functions["max_hp"] = { DoubleValue(pokemon.maxHealth.toDouble()) }
functions["current_hp"] = { DoubleValue(pokemon.currentHealth.toDouble()) }
functions["friendship"] = { DoubleValue(pokemon.friendship.toDouble()) }
functions["set_friendship"] = { params ->
val amount = params.getDouble(0)
pokemon.setFriendship(amount.toInt())
}
functions["max_fullness"] = { DoubleValue(pokemon.getMaxFullness().toDouble()) }
functions["fullness"] = { DoubleValue(pokemon.currentFullness.toDouble()) }
functions["lose_fullness"] = { params ->
@@ -452,6 +456,7 @@ object PokemonMoLangFunctions : AbstractMoLangFunctionHolder<Pokemon>() {
functions["entity"] = { pokemon.entity?.asMoLangValue() ?: DoubleValue.ZERO }
functions["is_alpha"] = { DoubleValue(pokemon.isAlpha) }
functions["size_category"] = { StringValue(pokemon.getSizeCategory().name) }
functions["full_heal"] = { pokemon.heal() }
return functions
}
@@ -8,27 +8,47 @@
package com.cobblemon.mod.common.api.pokeball
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokeball.catching.CaptureEffect
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.pokeball.catching.effects.CaptureEffects
import com.cobblemon.mod.common.api.pokeball.catching.effects.FriendshipEarningBoostEffect
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.*
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.BaseStatModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.CatchRateModifiers
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.DynamicMultiplierModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.GuaranteedModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.MultiplierModifier
import com.cobblemon.mod.common.api.pokemon.labels.CobblemonPokemonLabels
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.pokemon.status.Statuses
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.item.components.PokeBallComponent
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.adapters.CobblemonCaptureEffectAdapter
import com.cobblemon.mod.common.util.adapters.CobblemonCatchRateModifierAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionAdapter
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.FloatNumberRangeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.adapters.IntRangeAdapter
import com.cobblemon.mod.common.util.adapters.LiteralHexColorAdapter
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
import net.minecraft.advancements.critereon.MinMaxBounds
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.server.packs.PackType
import net.minecraft.util.LowerCaseEnumTypeAdapterFactory
import java.awt.Color
import kotlin.math.roundToInt
/**
@@ -43,6 +63,15 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
// ToDo once datapack pokeball is implemented add required adapters here
override val gson: Gson = GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(IntRange::class.java, IntRangeAdapter)
.registerTypeAdapter(MinMaxBounds.Doubles::class.java, FloatNumberRangeAdapter)
.registerTypeAdapter(CatchRateModifier::class.java, CobblemonCatchRateModifierAdapter)
.registerTypeAdapter(CaptureEffect::class.java, CobblemonCaptureEffectAdapter)
.registerTypeAdapter(Color::class.java, LiteralHexColorAdapter)
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
@@ -50,8 +79,8 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
override val resourcePath = "pokeballs"
private val defaults = hashMapOf<ResourceLocation, PokeBall>()
// ToDo datapack pokeball type here instead
private val custom = hashMapOf<ResourceLocation, PokeBall>()
val custom = hashMapOf<ResourceLocation, PokeBall>()
@JvmStatic
@get:JvmName("getPokeBall")
@@ -245,6 +274,10 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
@get:JvmName("getAncientOriginBall")
val ANCIENT_ORIGIN_BALL
get() = this.byName("ancient_origin_ball")
@JvmStatic
@get:JvmName("getStrangeBall")
val STRANGE_BALL
get() = this.byName("strange_ball")
init {
createDefault("poke_ball")
@@ -257,12 +290,12 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("ultra_ball", MultiplierModifier(2F))
createDefault("master_ball", GuaranteedModifier())
createDefault("safari_ball", CatchRateModifiers.SAFARI)
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, 100, BaseStatModifier.Comparator.GTE, 4F))
createDefault("level_ball", CatchRateModifiers.LEVEL)
createDefault("lure_ball", CatchRateModifiers.LURE)
createDefault("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
createDefault("love_ball", CatchRateModifiers.LOVE)
createDefault("friend_ball", effects = listOf(CaptureEffects.friendshipSetter(150)))
createDefault("friend_ball", effects = listOf("q.pokemon.set_friendship(150.0)".asExpressionLike()))
createDefault("moon_ball", CatchRateModifiers.MOON_PHASES)
createDefault("sport_ball", MultiplierModifier(1.5F))
createDefault("net_ball", CatchRateModifiers.typeBoosting(3F, ElementalTypes.BUG, ElementalTypes.WATER))
@@ -270,10 +303,10 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("nest_ball", CatchRateModifiers.NEST)
createDefault("repeat_ball", CatchRateModifiers.REPEAT)
createDefault("timer_ball", CatchRateModifiers.turnBased { turn -> (1F + turn * (1229F / 4096F)).coerceAtMost(4F) })
createDefault("luxury_ball", effects = listOf(FriendshipEarningBoostEffect(2F)))
createDefault("luxury_ball", /*effects = listOf(FriendshipEarningBoostEffect(2F))*/)
createDefault("premier_ball")
createDefault("dusk_ball", CatchRateModifiers.LIGHT_LEVEL)
createDefault("heal_ball", effects = listOf(CaptureEffects.FULL_RESTORE))
createDefault("heal_ball", effects = listOf("q.pokemon.heal()".asExpressionLike()))
createDefault("quick_ball", CatchRateModifiers.turnBased { turn -> if (turn == 1) 5F else 1F })
createDefault("cherish_ball")
createDefault("park_ball", CatchRateModifiers.PARK)
@@ -295,6 +328,9 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
createDefault("ancient_wing_ball", MultiplierModifier(1.5F), throwPower = 2.5f, ancient = true)
createDefault("ancient_jet_ball", MultiplierModifier(2F), throwPower = 2.5f, ancient = true)
createDefault("ancient_origin_ball", GuaranteedModifier(), ancient = true)
// for customs
createDefault("strange_ball")
// Luxury ball effect, low priority as it must be triggered before soothe bell as of gen 4
CobblemonEvents.FRIENDSHIP_UPDATED.subscribe(priority = Priority.LOW) { event ->
if (!event.earned) {
@@ -311,8 +347,20 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
}
override fun reload(data: Map<ResourceLocation, PokeBall>) {
data.entries.forEach { it.value.name = it.key }
this.custom.clear()
// ToDo once datapack pokeball is implemented load them here, we will want datapacks to be able to override our default pokeballs too, however they will never be able to disable them
data.forEach { (location, ball) ->
if (defaults.containsKey(location)) {
ball.stack = defaults[location]!!.stack
} else {
ball.stack = PokeBallComponent.createStack(ball)
}
this.custom[location] = ball
}
Cobblemon.LOGGER.info("Loaded ${data.size} custom poke balls.")
}
override fun sync(player: ServerPlayer) {
@@ -321,10 +369,11 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
/**
* Gets a Pokeball from registry name.
* @return the pokeball object if found otherwise null.
* @return the pokeball object if found otherwise strange ball.
*/
@JvmStatic
fun getPokeBall(name : ResourceLocation): PokeBall? = this.custom[name] ?: this.defaults[name]
fun getPokeBall(name : ResourceLocation): PokeBall =
this.custom[name] ?: this.defaults[name] ?: STRANGE_BALL
@JvmStatic
fun all() = this.defaults.filterKeys { !this.custom.containsKey(it) }.values + this.custom.values
@@ -332,213 +381,22 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
private fun createDefault(
name: String,
modifier: CatchRateModifier = MultiplierModifier(1F) { _, _ -> true },
effects: List<CaptureEffect> = emptyList(),
effects: List<ExpressionLike> = emptyList(),
waterDragValue: Float = 0.8F,
model2d: ResourceLocation = cobblemonResource(name),
model3d: ResourceLocation = cobblemonResource("item/${name}_model"),
throwPower: Float = 1.25f,
ancient: Boolean = false
ancient: Boolean = false,
color: Color = Color.decode("#ff1111")
): PokeBall {
val identifier = cobblemonResource(name)
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient)
val pokeball = PokeBall(identifier, modifier, effects, PokeBall.PokeBallProperties(), waterDragValue, model2d, model3d, throwPower, ancient, color)
this.defaults[identifier] = pokeball
return pokeball
}
private fun byName(name: String): PokeBall {
fun byName(name: String): PokeBall {
val identifier = cobblemonResource(name)
return this.custom[identifier] ?: this.defaults[identifier]!!
}
/**
* Backwards compatibility getters
*/
@JvmName("getPOKE_BALL")
@Deprecated("Use PokeBalls.getPokeBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getPOKE_BALL() = POKE_BALL
@JvmName("getSLATE_BALL")
@Deprecated("Use PokeBalls.getSlateBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getSLATE_BALL() = SLATE_BALL
@JvmName("getAZURE_BALL")
@Deprecated("Use PokeBalls.getAzureBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getAZURE_BALL() = AZURE_BALL
@JvmName("getVERDANT_BALL")
@Deprecated("Use PokeBalls.getVerdantBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getVERDANT_BALL() = VERDANT_BALL
@JvmName("getROSEATE_BALL")
@Deprecated("Use PokeBalls.getRoseateBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getROSEATE_BALL() = ROSEATE_BALL
@JvmName("getCITRINE_BALL")
@Deprecated("Use PokeBalls.getCitrineBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getCITRINE_BALL() = CITRINE_BALL
@JvmName("getGREAT_BALL")
@Deprecated("Use PokeBalls.getGreatBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getGREAT_BALL() = GREAT_BALL
@JvmName("getULTRA_BALL")
@Deprecated("Use PokeBalls.getUltraBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getULTRA_BALL() = ULTRA_BALL
@JvmName("getMASTER_BALL")
@Deprecated("Use PokeBalls.getMasterBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getMASTER_BALL() = MASTER_BALL
@JvmName("getSAFARI_BALL")
@Deprecated("Use PokeBalls.getSafariBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getSAFARI_BALL() = SAFARI_BALL
@JvmName("getFAST_BALL")
@Deprecated("Use PokeBalls.getFastBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getFAST_BALL() = FAST_BALL
@JvmName("getLEVEL_BALL")
@Deprecated("Use PokeBalls.getLevelBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getLEVEL_BALL() = LEVEL_BALL
@JvmName("getLURE_BALL")
@Deprecated("Use PokeBalls.getLureBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getLURE_BALL() = LURE_BALL
@JvmName("getHEAVY_BALL")
@Deprecated("Use PokeBalls.getHeavyBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getHEAVY_BALL() = HEAVY_BALL
@JvmName("getLOVE_BALL")
@Deprecated("Use PokeBalls.getLoveBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getLOVE_BALL() = LOVE_BALL
@JvmName("getFRIEND_BALL")
@Deprecated("Use PokeBalls.getFriendBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getFRIEND_BALL() = FRIEND_BALL
@JvmName("getMOON_BALL")
@Deprecated("Use PokeBalls.getMoonBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getMOON_BALL() = MOON_BALL
@JvmName("getSPORT_BALL")
@Deprecated("Use PokeBalls.getSportBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getSPORT_BALL() = SPORT_BALL
@JvmName("getNET_BALL")
@Deprecated("Use PokeBalls.getNetBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getNET_BALL() = NET_BALL
@JvmName("getNEST_BALL")
@Deprecated("Use PokeBalls.getNestBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getNEST_BALL() = NEST_BALL
@JvmName("getREPEAT_BALL")
@Deprecated("Use PokeBalls.getRepeatBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getREPEAT_BALL() = REPEAT_BALL
@JvmName("getTIMER_BALL")
@Deprecated("Use PokeBalls.getTimerBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getTIMER_BALL() = TIMER_BALL
@JvmName("getLUXURY_BALL")
@Deprecated("Use PokeBalls.getLuxuryBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getLUXURY_BALL() = LUXURY_BALL
@JvmName("getPREMIER_BALL")
@Deprecated("Use PokeBalls.getPremierBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getPREMIER_BALL() = PREMIER_BALL
@JvmName("getDUSK_BALL")
@Deprecated("Use PokeBalls.getDuskBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getDUSK_BALL() = DUSK_BALL
@JvmName("getHEAL_BALL")
@Deprecated("Use PokeBalls.getHealBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getHEAL_BALL() = HEAL_BALL
@JvmName("getQUICK_BALL")
@Deprecated("Use PokeBalls.getQuickBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getQUICK_BALL() = QUICK_BALL
@JvmName("getCHERISH_BALL")
@Deprecated("Use PokeBalls.getCherishBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getCHERISH_BALL() = CHERISH_BALL
@JvmName("getPARK_BALL")
@Deprecated("Use PokeBalls.getParkBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getPARK_BALL() = PARK_BALL
@JvmName("getDREAM_BALL")
@Deprecated("Use PokeBalls.getDreamBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getDREAM_BALL() = DREAM_BALL
@JvmName("getBEAST_BALL")
@Deprecated("Use PokeBalls.getBeastBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getBEAST_BALL() = BEAST_BALL
@JvmName("getANCIENT_POKE_BALL")
@Deprecated("Use PokeBalls.getAncientPokeBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_POKE_BALL() = ANCIENT_POKE_BALL
@JvmName("getANCIENT_CITRINE_BALL")
@Deprecated("Use PokeBalls.getAncientCitrineBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_CITRINE_BALL() = ANCIENT_CITRINE_BALL
@JvmName("getANCIENT_VERDANT_BALL")
@Deprecated("Use PokeBalls.getAncientVerdantBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_VERDANT_BALL() = ANCIENT_VERDANT_BALL
@JvmName("getANCIENT_AZURE_BALL")
@Deprecated("Use PokeBalls.getAncientAzureBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_AZURE_BALL() = ANCIENT_AZURE_BALL
@JvmName("getANCIENT_ROSEATE_BALL")
@Deprecated("Use PokeBalls.getAncientRoseateBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_ROSEATE_BALL() = ANCIENT_ROSEATE_BALL
@JvmName("getANCIENT_SLATE_BALL")
@Deprecated("Use PokeBalls.getAncientSlateBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_SLATE_BALL() = ANCIENT_SLATE_BALL
@JvmName("getANCIENT_IVORY_BALL")
@Deprecated("Use PokeBalls.getAncientIvoryBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_IVORY_BALL() = ANCIENT_IVORY_BALL
@JvmName("getANCIENT_GREAT_BALL")
@Deprecated("Use PokeBalls.getAncientGreatBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_GREAT_BALL() = ANCIENT_GREAT_BALL
@JvmName("getANCIENT_ULTRA_BALL")
@Deprecated("Use PokeBalls.getAncientUltraBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_ULTRA_BALL() = ANCIENT_ULTRA_BALL
@JvmName("getANCIENT_HEAVY_BALL")
@Deprecated("Use PokeBalls.getAncientHeavyBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_HEAVY_BALL() = ANCIENT_HEAVY_BALL
@JvmName("getANCIENT_LEADEN_BALL")
@Deprecated("Use PokeBalls.getAncientLeadenBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_LEADEN_BALL() = ANCIENT_LEADEN_BALL
@JvmName("getANCIENT_GIGATON_BALL")
@Deprecated("Use PokeBalls.getAncientGigatonBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_GIGATON_BALL() = ANCIENT_GIGATON_BALL
@JvmName("getANCIENT_FEATHER_BALL")
@Deprecated("Use PokeBalls.getAncientFeatherBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_FEATHER_BALL() = ANCIENT_FEATHER_BALL
@JvmName("getANCIENT_WING_BALL")
@Deprecated("Use PokeBalls.getAncientWingBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_WING_BALL() = ANCIENT_WING_BALL
@JvmName("getANCIENT_JET_BALL")
@Deprecated("Use PokeBalls.getAncientJetBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_JET_BALL() = ANCIENT_JET_BALL
@JvmName("getANCIENT_ORIGIN_BALL")
@Deprecated("Use PokeBalls.getAncientOriginBall(), provided for backwards compatibility until Cobblemon 1.8.")
fun getANCIENT_ORIGIN_BALL() = ANCIENT_ORIGIN_BALL
}
}
@@ -18,6 +18,10 @@ import net.minecraft.world.entity.LivingEntity
*
* @property multiplier The multiplier for friendship gains
*/
class FriendshipEarningBoostEffect(val multiplier: Float) : CaptureEffect {
data class FriendshipEarningBoostEffect(val multiplier: Float) : CaptureEffect {
companion object {
const val VARIANT = "friendship_boost"
}
override fun apply(thrower: LivingEntity, pokemon: Pokemon) {}
}
}
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.api.pokeball.catching.modifiers
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.util.StringRepresentable
import net.minecraft.world.entity.LivingEntity
/**
@@ -26,9 +27,13 @@ import net.minecraft.world.entity.LivingEntity
*/
class BaseStatModifier(
val stat: Stat,
val comparator: (value: Int) -> Boolean,
val multiplier: Float
val value: Int,
val comparator: Comparator,
val multiplier: Float,
) : CatchRateModifier {
companion object {
const val VARIANT = "bst"
}
init {
if (stat.type != Stat.Type.PERMANENT) {
@@ -44,9 +49,36 @@ class BaseStatModifier(
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean {
val valStat = pokemon.form.baseStats[this.stat] ?: return false
return comparator(valStat)
return when (comparator) {
Comparator.GT -> value > valStat
Comparator.LT -> value < valStat
Comparator.GTE -> value >= valStat
Comparator.LTE -> value <= valStat
Comparator.EQ -> value == valStat
Comparator.NOT -> value != valStat
}
}
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
override fun modifyCatchRate(
currentCatchRate: Float,
thrower: LivingEntity,
pokemon: Pokemon,
): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
enum class Comparator : StringRepresentable {
@JvmField
GT,
@JvmField
LT,
@JvmField
GTE,
@JvmField
LTE,
@JvmField
EQ,
@JvmField
NOT,
;
override fun getSerializedName(): String = this.name.lowercase()
}
}
@@ -0,0 +1,78 @@
/*
* 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.pokeball.catching.modifiers
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asBiomeMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asWorldMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.toBlockPos
import com.cobblemon.mod.common.util.withPlayerValue
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.core.registries.Registries
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
class ExpressionModifier(
val query: ExpressionLike = "1.0".asExpressionLike(),
) : CatchRateModifier {
companion object {
const val VARIANT = "expression"
val runtime = MoLangRuntime().setup()
}
override fun isGuaranteed(): Boolean = false
override fun value(
thrower: LivingEntity,
pokemon: Pokemon
): Float {
val worldValue =
thrower
.level()
.registryAccess()
.registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(thrower.level())
.asWorldMoLangValue()
val biomeValue = thrower.level().getBiome(thrower.position().toBlockPos()).asBiomeMoLangValue()
var ballRuntime = runtime
.withQueryValue("world", worldValue)
.withQueryValue("biome", biomeValue)
.withQueryValue("pokemon", pokemon.asStruct())
if (thrower is ServerPlayer) {
ballRuntime = ballRuntime.withPlayerValue("player", thrower)
}
return query.resolveFloat(ballRuntime)
}
override fun behavior(
thrower: LivingEntity,
pokemon: Pokemon
): CatchRateModifier.Behavior = CatchRateModifier.Behavior.MULTIPLY
override fun isValid(
thrower: LivingEntity,
pokemon: Pokemon
): Boolean = true
override fun modifyCatchRate(
currentCatchRate: Float,
thrower: LivingEntity,
pokemon: Pokemon
): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
}
@@ -19,11 +19,13 @@ import net.minecraft.world.entity.LivingEntity
* @since August 9th, 2022
*/
class GuaranteedModifier : CatchRateModifier {
companion object {
const val VARIANT = "guaranteed"
}
override fun isGuaranteed() = true
override fun value(thrower: LivingEntity, pokemon: Pokemon): Float = 255F
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior = CatchRateModifier.Behavior.MULTIPLY
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean = true
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
}
}
@@ -22,20 +22,22 @@ import net.minecraft.world.entity.LivingEntity
* @property matching Will this multiplier should be applied if the labels match?
* @property labels The literal labels being queried.
*/
class LabelModifier(
val multiplier: Float,
val matching: Boolean,
vararg val labels: String
) : CatchRateModifier {
data class LabelModifier(val multiplier: Float, val matching: Boolean, val labels: List<String>) : CatchRateModifier {
companion object {
const val VARIANT = "labels"
}
override fun isGuaranteed(): Boolean = false
override fun value(thrower: LivingEntity, pokemon: Pokemon): Float = this.multiplier
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior = CatchRateModifier.Behavior.MULTIPLY
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior =
CatchRateModifier.Behavior.MULTIPLY
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean = if (this.matching) pokemon.hasLabels(*this.labels) else !pokemon.hasLabels(*this.labels)
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean =
if (this.matching) pokemon.hasLabels(*this.labels.toSet().toTypedArray()) else !pokemon.hasLabels(*this.labels.toSet().toTypedArray())
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float =
this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
}
}
@@ -13,19 +13,21 @@ import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.entity.LivingEntity
class PropertyBasedModifier(
val property: PokemonProperties,
val multiplier: Float
) : CatchRateModifier {
data class PropertyBasedModifier(val property: PokemonProperties, val multiplier: Float) : CatchRateModifier {
companion object {
const val VARIANT = "property"
}
override fun isGuaranteed(): Boolean = false
override fun value(thrower: LivingEntity, pokemon: Pokemon): Float = this.multiplier
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior = CatchRateModifier.Behavior.MULTIPLY
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior =
CatchRateModifier.Behavior.MULTIPLY
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean = this.property.matches(pokemon)
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
override fun modifyCatchRate(currentCatchRate: Float, thrower: LivingEntity, pokemon: Pokemon): Float =
this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
}
@@ -0,0 +1,80 @@
/*
* 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.pokeball.catching.modifiers
import com.bedrockk.molang.runtime.MoLangRuntime
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asBiomeMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asStruct
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asWorldMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.toBlockPos
import com.cobblemon.mod.common.util.withPlayerValue
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.core.registries.Registries
import net.minecraft.resources.ResourceLocation
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.entity.LivingEntity
class ScriptModifier(
val location: ResourceLocation
) : CatchRateModifier {
companion object {
const val VARIANT = "script"
val runtime = MoLangRuntime().setup()
}
override fun isGuaranteed(): Boolean = false
override fun value(
thrower: LivingEntity,
pokemon: Pokemon
): Float {
val worldValue =
thrower
.level()
.registryAccess()
.registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(thrower.level())
.asWorldMoLangValue()
val biomeValue = thrower.level().getBiome(thrower.position().toBlockPos()).asBiomeMoLangValue()
var ballRuntime = runtime
.withQueryValue("world", worldValue)
.withQueryValue("biome", biomeValue)
.withQueryValue("pokemon", pokemon.asStruct())
if (thrower is ServerPlayer) {
ballRuntime = ballRuntime.withPlayerValue("player", thrower)
}
return CobblemonScripts.run(location, ballRuntime)?.asDouble()?.toFloat() ?: 0.0f
}
override fun behavior(
thrower: LivingEntity,
pokemon: Pokemon
): CatchRateModifier.Behavior = CatchRateModifier.Behavior.MULTIPLY
override fun isValid(
thrower: LivingEntity,
pokemon: Pokemon
): Boolean = true
override fun modifyCatchRate(
currentCatchRate: Float,
thrower: LivingEntity,
pokemon: Pokemon
): Float = this.behavior(thrower, pokemon).mutator(currentCatchRate, this.value(thrower, pokemon))
}
@@ -0,0 +1,34 @@
/*
* 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.pokeball.catching.modifiers
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.pokemon.Pokemon
import net.minecraft.world.entity.LivingEntity
data class SimpleModifier(val multiplier: Float) : CatchRateModifier {
companion object {
const val VARIANT = "simple"
}
override fun isGuaranteed(): Boolean = false
override fun value(thrower: LivingEntity, pokemon: Pokemon): Float = this.multiplier
override fun behavior(thrower: LivingEntity, pokemon: Pokemon): CatchRateModifier.Behavior =
CatchRateModifier.Behavior.MULTIPLY
override fun isValid(thrower: LivingEntity, pokemon: Pokemon): Boolean = true
override fun modifyCatchRate(
currentCatchRate: Float,
thrower: LivingEntity,
pokemon: Pokemon,
): Float = behavior(thrower, pokemon).mutator(currentCatchRate, value(thrower, pokemon))
}
@@ -72,30 +72,6 @@ object Dexes : JsonDataRegistry<PokedexDef> {
return findDexEntryBySpeciesId(cobblemonResource("national"), speciesId)
}
fun findRootDexEntryBySpeciesId(speciesId: ResourceLocation, formName: String): Pair<PokedexEntry, PokedexForm> {
var rootSpecies = PokemonSpecies.getByIdentifier(speciesId)!!
val initialFormEquivalents = getFormEquivalents(formName)
var rootForm = rootSpecies.forms.firstOrNull { initialFormEquivalents.contains(it.name.lowercase()) }
?: rootSpecies.standardForm
var preEvolution = rootForm.preEvolution
while (preEvolution != null) {
rootSpecies = preEvolution.species
rootForm = preEvolution.resolveForm(rootForm.aspects.toSet())
preEvolution = rootForm.preEvolution
}
val entry = findDexEntryBySpeciesId(rootSpecies.resourceIdentifier)
?: throw RuntimeException("Root dex entry not found")
val formEquivalents = getFormEquivalents(rootForm.name)
val form =
entry.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) } ?:
entry.forms.first { it.displayForm.equals("Normal", ignoreCase = true) }
return entry to form
}
fun getFormEquivalents(formName: String): List<String> {
val key = equivalentFormsLookup[formName.lowercase()]
return equivalentForms[key] ?: listOf(formName.lowercase())
@@ -8,13 +8,20 @@
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.reactive.SimpleObservable
import com.cobblemon.mod.common.net.messages.client.data.DexEntrySyncPacket
import com.cobblemon.mod.common.net.messages.client.data.DexEvolutionDisplaySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.util.adapters.ExpressionLikeAdapter
import com.cobblemon.mod.common.util.adapters.IdentifierAdapter
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.lang
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.reflect.TypeToken
@@ -38,6 +45,8 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
val entries = mutableMapOf<ResourceLocation, PokedexEntry>()
val evolutionDisplays = mutableListOf<DexEvolutionDisplay>()
override fun reload(data: Map<ResourceLocation, PokedexEntry>) {
data.forEach { _, entry ->
entries[entry.id] = entry
@@ -50,6 +59,7 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
}
}
}
computeEvolutionDisplays()
observable.emit(this)
}
@@ -57,5 +67,58 @@ object DexEntries : JsonDataRegistry<PokedexEntry> {
override fun sync(player: ServerPlayer) {
DexEntrySyncPacket(entries.values).sendToPlayer(player)
DexEvolutionDisplaySyncPacket(evolutionDisplays).sendToPlayer(player)
}
}
fun computeEvolutionDisplays() {
evolutionDisplays.clear()
PokemonSpecies.species.forEach {
try {
evolutionDisplays.addAll(buildDisplays(it.standardForm) + it.forms.flatMap { form -> buildDisplays(form) })
} catch (e: Exception) {
Cobblemon.LOGGER.error("Failed to build dex evolution displays for ${it.resourceIdentifier}", e)
}
}
}
fun buildDisplays(form: FormData): List<DexEvolutionDisplay> {
val result = mutableListOf<DexEvolutionDisplay>()
(form.ownEvolutions ?: form.evolutions).forEach { evolution ->
val requirements = mutableListOf<DexEvolutionRequirementDisplay>()
getToolTipHeaderIcon(evolution)?.let { header ->
requirements.add(header)
}
evolution.requirements.mapNotNull { resolveRequirementData(it) }.forEach { requirement ->
requirements.add(requirement)
}
listOfNotNull(evolution.result, evolution.shedder).forEach resultLoop@{ resultProperties ->
val resultSpecies = resultProperties.species
?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
?: return@resultLoop
// The result's own aspects win, otherwise carry the source form's aspects over
// like a real evolution does (Alola-Bias Pichu -> Alola-Bias Pikachu)
val resultForm = resultSpecies.getForm(resultProperties.aspects).takeIf { it != resultSpecies.standardForm }
?: resultSpecies.getForm(form.aspects.toSet() + resultProperties.aspects)
val toAspects = resultProperties.aspects + resultForm.aspects
val resultRequirements = if (resultProperties === evolution.shedder) {
requirements + CobblemonDexEvolutionRequirementDisplay(CobblemonItems.POKE_BALL, lang("ui.pokedex.evolution.shedder.tooltip"))
} else {
requirements
}
result.add(DexEvolutionDisplay(
fromSpecies = form.species.resourceIdentifier,
fromForm = form.name,
fromAspects = form.aspects.toSet(),
toSpecies = resultSpecies.resourceIdentifier,
toForm = resultForm.name,
toAspects = toAspects,
requirements = resultRequirements,
))
}
}
return result
}
}
@@ -0,0 +1,466 @@
/*
* 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.pokedex.entry
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeTagCondition
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.pokemon.requirements.*
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.core.registries.Registries
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.resources.ResourceLocation
import net.minecraft.util.ExtraCodecs
import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.levelgen.structure.Structure
import kotlin.jvm.optionals.getOrNull
data class DexEvolutionDisplay(
val fromSpecies: ResourceLocation,
val fromForm: String,
val fromAspects: Set<String>,
val toSpecies: ResourceLocation,
val toForm: String,
val toAspects: Set<String>,
val requirements: List<DexEvolutionRequirementDisplay>,
)
interface DexEvolutionRequirementDisplay {
val icon: Item
val tooltip: MutableComponent
}
data class CobblemonDexEvolutionRequirementDisplay(
override val icon: Item,
override val tooltip: MutableComponent,
) : DexEvolutionRequirementDisplay
data class CobblemonBiomeDexEvolutionRequirementDisplay(
override val icon: Item,
override val tooltip: MutableComponent,
val condition: ExtraCodecs.TagOrElementLocation? = null,
val antiCondition: ExtraCodecs.TagOrElementLocation? = null,
) : DexEvolutionRequirementDisplay
fun getToolTipHeaderIcon(evolution: Evolution): DexEvolutionRequirementDisplay? {
when (evolution) {
is ItemInteractionEvolution -> return getToolTipHeaderIcon(evolution)
is TradeEvolution -> return getToolTipHeaderIcon(evolution)
}
return null
}
fun getToolTipHeaderIcon(itemInteractionEvolution: ItemInteractionEvolution): CobblemonDexEvolutionRequirementDisplay? {
val item = itemInteractionEvolution.requiredContext.items.orElse(null)?.firstOrNull()?.value() ?: return null
return CobblemonDexEvolutionRequirementDisplay(item, lang("ui.pokedex.evolution.item_interact.tooltip", item.description.plainCopy().green()))
}
fun getToolTipHeaderIcon(tradeEvolution: TradeEvolution): CobblemonDexEvolutionRequirementDisplay {
val species = tradeEvolution.requiredContext.species
val tooltip = if (species == null) {
lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
} else {
PokemonSpecies.getByIdentifier(species.asIdentifierDefaultingNamespace())
?.let { lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()) }
?: lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
}
return CobblemonDexEvolutionRequirementDisplay(CobblemonItems.LINK_CABLE, tooltip)
}
fun resolveRequirementData(requirement: Requirement): DexEvolutionRequirementDisplay? {
val icon = requirement.pokedexIcon?.let {
RegistryAccessResolver.getAccess().registryOrThrow(Registries.ITEM).get(it)
}
val tooltip = requirement.pokedexTooltip?.let {
Component.translatable(it)
}
if (icon != null && tooltip != null) {
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
return buildRequirementData(requirement)
}
fun buildRequirementData(requirement: Requirement): DexEvolutionRequirementDisplay? {
when (requirement) {
is AdvancementRequirement -> return buildAdvancementRequirement(requirement)
is BattleCriticalHitsRequirement -> return buildBattleCriticalHitsRequirement(requirement)
is BiomeRequirement -> return buildBiomeRequirement(requirement)
is BlocksTraveledRequirement -> return buildBlocksTraveledRequirement(requirement)
is DamageTakenRequirement -> return buildDamageTakenRequirement(requirement)
is DefeatRequirement -> return buildDefeatRequirement(requirement)
is FriendshipRequirement -> return buildFriendshipRequirement(requirement)
is HeldItemRequirement -> return buildHeldItemRequirement(requirement)
is LevelRequirement -> return buildLevelRequirement(requirement)
is MoonPhaseRequirement -> return buildMoonPhaseRequirement(requirement)
is MoveSetRequirement -> return buildMoveSetRequirement(requirement)
is MoveTypeRequirement -> return buildMoveTypeRequirement(requirement)
is NegateRequirement -> return buildNegateRequirement(requirement)
is OwnerHoldsItemRequirement -> return buildOwnerHoldsItemRequirement(requirement)
is PartyMemberRequirement -> return buildPartyMemberRequirement(requirement)
is PokemonPropertiesRequirement -> return buildPokemonPropertiesRequirement(requirement)
is PropertyRangeRequirement -> return buildPropertyRangeRequirement(requirement)
is RecoilRequirement -> return buildRecoilRequirement(requirement)
is StatCompareRequirement -> return buildStatCompareRequirement(requirement)
is StatEqualRequirement -> return buildStatEqualRequirement(requirement)
is StructureRequirement -> return buildStructureRequirement(requirement)
is TimeRangeRequirement -> return buildTimeRangeRequirement(requirement)
is UseMoveRequirement -> return buildUseMoveRequirement(requirement)
is WeatherRequirement -> return buildWeatherRequirement(requirement)
}
return null
}
fun buildAdvancementRequirement(advancementRequirement: AdvancementRequirement): DexEvolutionRequirementDisplay {
val icon = Items.BOOK
val identifier = advancementRequirement.requiredAdvancement
val advancementLanguageKey = "${identifier.namespace}.${identifier.path.substringAfterLast('/')}"
val advancementComponent = Component.translatable("advancements.$advancementLanguageKey").green()
val tooltip = lang("ui.pokedex.requirement.advancement.tooltip", advancementComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildBattleCriticalHitsRequirement(battleCriticalHitsRequirement: BattleCriticalHitsRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.MEDICINAL_LEEK
val criticalsComponent = Component.literal(battleCriticalHitsRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.battle_critical_hits.tooltip", criticalsComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
private fun tagDisplayName(registry: String, id: ResourceLocation): MutableComponent {
return Component.translatableWithFallback("tag.$registry.${id.namespace}.${id.path.replace('/', '.')}", prettifyPath(id.path))
}
private fun prettifyPath(path: String): String {
return path.substringAfterLast('/').removePrefix("is_").split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) }
}
fun buildNegateRequirement(negateRequirement: NegateRequirement): DexEvolutionRequirementDisplay? {
val inner = negateRequirement.inner
when (inner) {
is BiomeRequirement -> buildBiomeRequirement(inner, negated = true)?.let { return it }
is StructureRequirement -> buildStructureRequirement(inner, negated = true)?.let { return it }
}
val innerDisplay = resolveRequirementData(inner) ?: return null
return CobblemonDexEvolutionRequirementDisplay(innerDisplay.icon, lang("ui.pokedex.requirement.negate.tooltip", innerDisplay.tooltip))
}
fun buildBiomeRequirement(biomeRequirement: BiomeRequirement, negated: Boolean = false): DexEvolutionRequirementDisplay? {
// Negating "in X" is "not in X"; negating both a condition and an anticondition isn't expressible as one line
if (negated && biomeRequirement.biomeCondition != null && biomeRequirement.biomeAnticondition != null) return null
val condition = if (negated) biomeRequirement.biomeAnticondition else biomeRequirement.biomeCondition
val antiCondition = if (negated) biomeRequirement.biomeCondition else biomeRequirement.biomeAnticondition
if (condition == null && antiCondition == null) {
return null
}
val icon = Items.COMPASS
val displayName: (RegistryLikeCondition<Biome>) -> MutableComponent = {
when (it) {
is RegistryLikeTagCondition -> tagDisplayName("worldgen.biome", it.tag.location())
is RegistryLikeIdentifierCondition -> Component.translatable("biome.${it.identifier.namespace}.${it.identifier.path}")
else -> Component.literal(it.toString())
}
}
val tooltip = if (condition != null && antiCondition != null) {
lang("ui.pokedex.requirement.biome.both.tooltip", displayName(condition).green(), displayName(antiCondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.biome.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.biome.anticondition.tooltip", displayName(antiCondition!!).green())
}
var conditionConverted: ExtraCodecs.TagOrElementLocation? = null
if (condition != null) {
val conditionIdentifier = if (condition is RegistryLikeIdentifierCondition) condition.identifier else (condition as RegistryLikeTagCondition).tag.location
conditionConverted = ExtraCodecs.TagOrElementLocation(conditionIdentifier, condition is RegistryLikeTagCondition)
}
var antiConditionConverted: ExtraCodecs.TagOrElementLocation? = null
if (antiCondition != null) {
val conditionIdentifier = if (antiCondition is RegistryLikeIdentifierCondition) antiCondition.identifier else (antiCondition as RegistryLikeTagCondition).tag.location
antiConditionConverted = ExtraCodecs.TagOrElementLocation(conditionIdentifier, antiCondition is RegistryLikeTagCondition)
}
return CobblemonBiomeDexEvolutionRequirementDisplay(icon, tooltip, conditionConverted, antiConditionConverted)
}
fun buildBlocksTraveledRequirement(blocksTraveledRequirement: BlocksTraveledRequirement): DexEvolutionRequirementDisplay {
val icon = Items.LEATHER_BOOTS
val blocksComponent = Component.literal(blocksTraveledRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.blocks_traveled.tooltip", blocksComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildDamageTakenRequirement(damageTakenRequirement: DamageTakenRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.PUNCHING_GLOVE
val blocksComponent = Component.literal(damageTakenRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.damage_taken.tooltip", blocksComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildDefeatRequirement(defeatRequirement: DefeatRequirement): DexEvolutionRequirementDisplay? {
val description = describePokemonProperties(defeatRequirement.target) ?: return null
val icon = CobblemonItems.PUNCHING_GLOVE
val defeatedAmount = defeatRequirement.amount
val descriptionComponent = Component.literal(defeatedAmount.toString()).green().add(" ").add(description)
val tooltip = lang("ui.pokedex.requirement.defeat.tooltip", descriptionComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildFriendshipRequirement(friendshipRequirement: FriendshipRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.SOOTHE_BELL
val friendshipAmountComponent = Component.literal(friendshipRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.friendship.tooltip", friendshipAmountComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildHeldItemRequirement(heldItemRequirement: HeldItemRequirement): DexEvolutionRequirementDisplay? {
val items = heldItemRequirement.itemCondition.items.getOrNull() ?: return null
val item = items.firstOrNull()?.value() ?: return null
val itemComponent = items.unwrapKey().map { tag ->
val id = tag.location()
val fallback = id.path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) }
Component.translatableWithFallback("tag.item.${id.namespace}.${id.path.replace('/', '.')}", fallback)
}.orElseGet { item.description.plainCopy() }
val tooltip = lang("ui.pokedex.requirement.held_item.tooltip", itemComponent.green())
return CobblemonDexEvolutionRequirementDisplay(item, tooltip)
}
fun buildLevelRequirement(levelRequirement: LevelRequirement): DexEvolutionRequirementDisplay {
val minLevel = levelRequirement.minLevel
val maxLevel = levelRequirement.maxLevel
val icon = CobblemonItems.RARE_CANDY
val tooltip =
if (minLevel == maxLevel || maxLevel == Int.MAX_VALUE)
lang("ui.pokedex.requirement.level.tooltip.min", Component.literal(minLevel.toString()).green())
else
lang("ui.pokedex.requirement.level.tooltip.between", Component.literal(minLevel.toString()).green(), Component.literal(maxLevel.toString()).green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): DexEvolutionRequirementDisplay {
val icon = Items.CLOCK
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildMoveSetRequirement(moveSetRequirement: MoveSetRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.TECHNICAL_MACHINE
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): DexEvolutionRequirementDisplay {
val type = ElementalTypes.get(moveTypeRequirement.type.name)
val icon = when (type) {
ElementalTypes.NORMAL -> CobblemonItems.NORMAL_GEM
ElementalTypes.FIRE -> CobblemonItems.FIRE_GEM
ElementalTypes.WATER -> CobblemonItems.WATER_GEM
ElementalTypes.GRASS -> CobblemonItems.GRASS_GEM
ElementalTypes.ELECTRIC -> CobblemonItems.ELECTRIC_GEM
ElementalTypes.ICE -> CobblemonItems.ICE_GEM
ElementalTypes.FIGHTING -> CobblemonItems.FIGHTING_GEM
ElementalTypes.POISON -> CobblemonItems.POISON_GEM
ElementalTypes.GROUND -> CobblemonItems.GROUND_GEM
ElementalTypes.FLYING -> CobblemonItems.FLYING_GEM
ElementalTypes.PSYCHIC -> CobblemonItems.PSYCHIC_GEM
ElementalTypes.BUG -> CobblemonItems.BUG_GEM
ElementalTypes.ROCK -> CobblemonItems.ROCK_GEM
ElementalTypes.GHOST -> CobblemonItems.GHOST_GEM
ElementalTypes.DRAGON -> CobblemonItems.DRAGON_GEM
ElementalTypes.DARK -> CobblemonItems.DARK_GEM
ElementalTypes.STEEL -> CobblemonItems.STEEL_GEM
ElementalTypes.FAIRY -> CobblemonItems.FAIRY_GEM
else -> CobblemonItems.NORMAL_GEM
}
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildOwnerHoldsItemRequirement(ownerHoldsItemRequirement: OwnerHoldsItemRequirement): DexEvolutionRequirementDisplay? {
val item = ownerHoldsItemRequirement.itemCondition.items.orElse(null)?.firstOrNull()?.value() ?: return null
val tooltip = lang("ui.pokedex.requirement.owner_held_item.tooltip", item.description.plainCopy().green())
return CobblemonDexEvolutionRequirementDisplay(item, tooltip)
}
fun buildPartyMemberRequirement(partyMemberRequirement: PartyMemberRequirement): DexEvolutionRequirementDisplay? {
val description = describePokemonProperties(partyMemberRequirement.target) ?: return null
val icon = CobblemonItems.POKE_BALL
val tooltip = if (partyMemberRequirement.contains) {
lang("ui.pokedex.requirement.party_member.tooltip", description)
} else {
lang("ui.pokedex.requirement.party_member.anticondition.tooltip", description)
}
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildPokemonPropertiesRequirement(pokemonPropertiesRequirement: PokemonPropertiesRequirement): DexEvolutionRequirementDisplay? {
val description = describePokemonProperties(pokemonPropertiesRequirement.target) ?: return null
val icon = Items.NAME_TAG
val tooltip = lang("ui.pokedex.requirement.pokemon_properties.tooltip", description)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildPropertyRangeRequirement(propertyRangeRequirement: PropertyRangeRequirement): DexEvolutionRequirementDisplay? {
val provider = SpeciesFeatures.getFeature(propertyRangeRequirement.feature) as? IntSpeciesFeatureProvider ?: return null
val displayName = provider.display?.name?.takeIf { it.isNotBlank() }?.asTranslated()?.green() ?: return null
val icon = provider.itemPoints.keys.firstOrNull()?.let { BuiltInRegistries.ITEM.get(it) } ?: Items.NAME_TAG
val range = propertyRangeRequirement.range
val tooltip = lang(
"ui.pokedex.requirement.property_range.tooltip",
displayName,
Component.literal(range.first.toString()).green(),
Component.literal(range.last.toString()).green()
)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildRecoilRequirement(recoilRequirement: RecoilRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.LIFE_ORB
val recoilComponent = Component.literal(recoilRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.recoil.tooltip", recoilComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildStatCompareRequirement(statCompareRequirement: StatCompareRequirement): DexEvolutionRequirementDisplay {
val highStat = Stats.getStat(statCompareRequirement.highStat)
val lowStat = Stats.getStat(statCompareRequirement.lowStat)
val icon = when (highStat) {
Stats.HP, Stats.EVASION, Stats.ACCURACY -> CobblemonItems.HEALTH_MOCHI
Stats.ATTACK -> CobblemonItems.MUSCLE_MOCHI
Stats.DEFENCE -> CobblemonItems.RESIST_MOCHI
Stats.SPECIAL_ATTACK -> CobblemonItems.GENIUS_MOCHI
Stats.SPECIAL_DEFENCE -> CobblemonItems.CLEVER_MOCHI
Stats.SPEED -> CobblemonItems.SWIFT_MOCHI
}
val highStatComponent = highStat.displayName.plainCopy().green()
val lowStatComponent = lowStat.displayName.plainCopy().green()
val tooltip = lang("ui.pokedex.requirement.stat_compare.tooltip", highStatComponent, lowStatComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildStatEqualRequirement(statEqualRequirement: StatEqualRequirement): DexEvolutionRequirementDisplay {
val statOne = Stats.getStat(statEqualRequirement.statOne)
val statTwo = Stats.getStat(statEqualRequirement.statTwo)
val statOneComponent = statOne.displayName.plainCopy().green()
val statTwoComponent = statTwo.displayName.plainCopy().green()
val icon = CobblemonItems.FRESH_START_MOCHI
val tooltip = lang("ui.pokedex.requirement.stat_equal.tooltip", statOneComponent, statTwoComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildStructureRequirement(structureRequirement: StructureRequirement, negated: Boolean = false): DexEvolutionRequirementDisplay? {
if (negated && structureRequirement.structureCondition != null && structureRequirement.structureAnticondition != null) return null
val condition = if (negated) structureRequirement.structureAnticondition else structureRequirement.structureCondition
val anticondition = if (negated) structureRequirement.structureCondition else structureRequirement.structureAnticondition
if (condition == null && anticondition == null) {
return null
}
val icon = Items.FILLED_MAP
val displayName: (RegistryLikeCondition<Structure>) -> MutableComponent = {
when (it) {
is RegistryLikeTagCondition -> tagDisplayName("worldgen.structure", it.tag.location())
is RegistryLikeIdentifierCondition -> Component.literal(prettifyPath(it.identifier.path))
else -> Component.literal(it.toString())
}
}
val tooltip = if (condition != null && anticondition != null) {
lang("ui.pokedex.requirement.structure.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.structure.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.structure.anticondition.tooltip", displayName(anticondition!!).green())
}
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): DexEvolutionRequirementDisplay? {
val icon = Items.CLOCK
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildUseMoveRequirement(useMoveRequirement: UseMoveRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.CHOICE_BAND
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
fun buildWeatherRequirement(weatherRequirement: WeatherRequirement): DexEvolutionRequirementDisplay {
val icon: Item
val tooltip: MutableComponent
if (weatherRequirement.isThundering == true) {
icon = Items.LIGHTNING_ROD
tooltip = lang("ui.pokedex.requirement.weather.tooltip.thundering")
} else {
icon = Items.WATER_BUCKET
tooltip = lang("ui.pokedex.requirement.weather.tooltip.raining")
}
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
@@ -6,7 +6,7 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
package com.cobblemon.mod.common.api.pokedex.entry
import com.cobblemon.mod.common.api.pokemon.Natures
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -28,7 +28,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.Items
internal fun describePokemonProperties(properties: PokemonProperties): MutableComponent? {
fun describePokemonProperties(properties: PokemonProperties): MutableComponent? {
if (hasUndescribedProperty(properties)) {
return null
}
@@ -26,7 +26,6 @@ import com.cobblemon.mod.common.api.moves.MoveTemplate
import com.cobblemon.mod.common.api.moves.MovesetBuilder
import com.cobblemon.mod.common.api.moves.adapters.MoveTemplateAdapter
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies.getByIdentifier
import com.cobblemon.mod.common.api.pokemon.effect.ShoulderEffect
import com.cobblemon.mod.common.api.pokemon.effect.adapter.ShoulderEffectAdapter
import com.cobblemon.mod.common.api.pokemon.egg.EggGroup
@@ -44,9 +43,6 @@ import com.cobblemon.mod.common.api.riding.sound.RideSoundSettingsList
import com.cobblemon.mod.common.api.spawning.TimeRange
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.adapters.ElementalTypeAdapter
import com.cobblemon.mod.common.net.messages.client.data.FormEvolutionOverride
import com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionEntry
import com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket
import com.cobblemon.mod.common.net.messages.client.data.SpeciesRegistrySyncPacket
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
@@ -246,18 +242,6 @@ object PokemonSpecies : JsonDataRegistry<Species> {
override fun sync(player: ServerPlayer) {
SpeciesRegistrySyncPacket(species.toList()).sendToPlayer(player)
// Sent after the species themselves so every species is already registered by the time this is decoded.
val evolutionEntries = species.map { singleSpecies ->
val formOverrides = singleSpecies.forms.mapNotNull { form ->
if (form.ownEvolutions == null && form.ownPreEvolution == null) {
null
} else {
FormEvolutionOverride(form.name, form.ownEvolutions, form.ownPreEvolution)
}
}
SpeciesEvolutionEntry(singleSpecies.resourceIdentifier, singleSpecies.evolutions, singleSpecies.preEvolution, formOverrides)
}
SpeciesEvolutionSyncPacket(evolutionEntries).sendToPlayer(player)
}
/**
@@ -25,7 +25,9 @@ import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.tags.CobblemonItemTags
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.item.components.PokeBallComponent
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
@@ -138,7 +140,7 @@ interface Evolution : EvolutionLike {
val shedPokemon = pokemon.clone(registryAccess = owner.registryAccess())
shedPokemon.removeHeldItem()
innerShedder.apply(shedPokemon)
shedPokemon.caughtBall = ((pokeballStack?.item ?: CobblemonItems.POKE_BALL) as PokeBallItem).pokeBall
shedPokemon.caughtBall = pokeballStack?.let { PokeBallComponent.getPokeBall(it) } ?: CobblemonItems.STRANGE_BALL.pokeBall
pokemon.storeCoordinates.get()?.store?.add(shedPokemon)
CobblemonCriteria.EVOLVE_POKEMON.trigger(owner, EvolvePokemonContext(pokemon.preEvolution!!.species.resourceIdentifier, shedPokemon.species.resourceIdentifier, playerDataManager.getGenericData(owner).advancementData.totalEvolvedCount))
// Consume one of the balls (if the player isn't creative)
@@ -25,8 +25,6 @@ interface PreEvolution {
val form: FormData
fun resolveForm(extraAspects: Set<String> = emptySet()): FormData = form
companion object {
fun of(species: Species, form: FormData = species.standardForm): PreEvolution = StandardPreEvolution(species, form)
@@ -145,7 +145,7 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<CompoundTag> {
// result.nickname = this.find(nbt, "Nickname", NbtCompound::getString)
val ball = this.find(nbt, "CaughtBall", CompoundTag::getString)
result.caughtBall = if(ball != null) PokeBalls.getPokeBall(ResourceLocation.parse(ball)) ?: PokeBalls.POKE_BALL else PokeBalls.POKE_BALL
result.caughtBall = if(ball != null) PokeBalls.getPokeBall(ResourceLocation.parse(ball)) else PokeBalls.STRANGE_BALL
return result
}
@@ -190,4 +190,4 @@ class ReforgedConversion(val base: Path) : CobblemonConverter<CompoundTag> {
CAREFUL,
}
}
}
@@ -16,7 +16,9 @@ import com.cobblemon.mod.common.client.render.pokeball.PokeBallPosableState
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity.CaptureState
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity.CaptureState.NOT
import com.cobblemon.mod.common.util.toRGBA
import net.minecraft.network.syncher.EntityDataAccessor
import org.joml.Vector4f
class EmptyPokeBallClientDelegate : PokeBallPosableState(), EntitySideDelegate<EmptyPokeBallEntity> {
override val stateEmitter: SettableObservable<CaptureState> = SettableObservable(NOT)
@@ -52,4 +54,8 @@ class EmptyPokeBallClientDelegate : PokeBallPosableState(), EntitySideDelegate<E
EmptyPokeBallEntity.ASPECTS -> currentAspects = currentEntity.aspects
}
}
}
fun getBeamColor(): Vector4f {
return currentEntity.pokeBall.beamColor.rgb.toRGBA()
}
}
@@ -56,7 +56,8 @@ class PCGUI(
val party: ClientParty,
val configuration: PCGUIConfiguration,
val openOnBox: Int = CobblemonClient.lastPcBoxViewed,
val unseenWallpapers: MutableSet<ResourceLocation> = mutableSetOf()
val unseenWallpapers: MutableSet<ResourceLocation> = mutableSetOf(),
val silent: Boolean = false
) : Screen(Component.translatable("cobblemon.ui.pc.title")), CobblemonRenderable {
companion object {
@@ -703,7 +704,7 @@ class PCGUI(
}
fun closeNormally(unlink: Boolean = true) {
playSound(CobblemonSounds.PC_OFF)
if (!silent) playSound(CobblemonSounds.PC_OFF)
Minecraft.getInstance().setScreen(null)
if (unlink) {
UnlinkPlayerFromPCPacket().sendToServer()
@@ -748,7 +749,7 @@ class PCGUI(
if (isInventoryKeyPressed(minecraft, keyCode, scanCode) && !boxNameFocused && !filterFocused) {
saveMarkings(isPreviewInParty ?: false)
playSound(CobblemonSounds.PC_OFF)
if (!silent) playSound(CobblemonSounds.PC_OFF)
UnlinkPlayerFromPCPacket().sendToServer()
Minecraft.getInstance().setScreen(null)
return true
@@ -759,7 +760,7 @@ class PCGUI(
if (!filterFocused && !boxNameFocused) {
when (keyCode) {
InputConstants.KEY_ESCAPE -> {
playSound(CobblemonSounds.PC_OFF)
if (!silent) playSound(CobblemonSounds.PC_OFF)
UnlinkPlayerFromPCPacket().sendToServer()
onClose()
return true
@@ -831,4 +832,4 @@ class PCGUI(
markingsWidget.setActivePokemon(null)
}
}
}
}
@@ -38,7 +38,6 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_HEIG
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.BASE_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HALF_OVERLAY_WIDTH
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.HEADER_BAR_HEIGHT
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.ICONS_ARROW_PADDING_AROUND
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.ICONS_PADDING_AROUND
@@ -52,8 +51,7 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_MOVES
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_SIZE
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.TAB_STATS
import com.cobblemon.mod.common.client.gui.pokedex.widgets.*
import com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree.EvolutionTreeWidget
import com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree.debugEvolutionRequirementRendering
import com.cobblemon.mod.common.client.gui.pokedex.widgets.EvolutionTreeWidget
import com.cobblemon.mod.common.client.pokedex.PokedexType
import com.cobblemon.mod.common.client.render.drawScaledText
import com.cobblemon.mod.common.net.messages.server.block.AdjustBlockEntityViewerCountPacket
@@ -194,6 +192,7 @@ class PokedexGUI private constructor(
public override fun init() {
super.init()
clearWidgets()
evolutionTreeExpanded = false
availableRegions = Dexes.dexEntryMap.keys.toList()
selectedRegionIndex = 0
@@ -828,10 +827,6 @@ class PokedexGUI private constructor(
}
}
if (tabInfoIndex == TAB_EVOLUTION_TREE && keyCode == InputConstants.KEY_SEMICOLON) {
debugEvolutionRequirementRendering()
}
return super.keyPressed(keyCode, scanCode, modifiers)
}
@@ -16,13 +16,17 @@ import com.cobblemon.mod.common.util.cobblemonResource
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.GuiGraphics
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.network.chat.Style
import net.minecraft.resources.ResourceLocation
import java.util.Optional
private val tooltipEdge = cobblemonResource("textures/gui/pokedex/tooltip_edge.png")
private val tooltipBackground = cobblemonResource("textures/gui/pokedex/tooltip_background.png")
private const val TOOLTIP_SLICE_SIZE = 11
private const val TOOLTIP_MAX_TEXT_WIDTH = 160
fun renderTooltip(
context: GuiGraphics,
@@ -49,7 +53,12 @@ fun renderTooltip(
posY: Int,
delta: Float,
offsetY: Int = 0,
maxTextWidth: Int = TOOLTIP_MAX_TEXT_WIDTH,
) {
val text = text.flatMap { wrapTooltipLine(it, maxTextWidth) }
// Only a tooltip that fits on a single line is centered; wrapped text is left-aligned
val centered = text.size == 1
val poseStack = context.pose()
val textWidth = text.maxOf { Minecraft.getInstance().font.width(it.font(CobblemonResources.DEFAULT_LARGE)) }
val tooltipPadding = 6
@@ -78,8 +87,6 @@ fun renderTooltip(
blitSlicedVertical(poseStack, tooltipBackground, posX - (tooltipWidth / 2), tooltipTop, tooltipWidth, totalHeight)
blitSlicedVertical(poseStack, tooltipEdge, posX + (tooltipWidth / 2), tooltipTop, 1, totalHeight)
val centered = text.size == 1
text.forEachIndexed { index, it ->
drawScaledText(
context = context,
@@ -95,6 +102,20 @@ fun renderTooltip(
poseStack.popPose()
}
private fun wrapTooltipLine(line: MutableComponent, maxWidth: Int): List<MutableComponent> {
val styled = line.copy().font(CobblemonResources.DEFAULT_LARGE)
if (Minecraft.getInstance().font.width(styled) <= maxWidth) return listOf(line)
return Minecraft.getInstance().font.splitter.splitLines(styled, maxWidth, Style.EMPTY).map { wrapped ->
val component = Component.empty()
wrapped.visit({ style, string ->
component.append(Component.literal(string).withStyle(style))
Optional.empty<Unit>()
}, Style.EMPTY)
component
}
}
// Stretching the whole 11px image over an arbitrary height thickens the caps as more lines are added, so
// instead the caps are drawn 1:1 and only the flat fill row is stretched to cover the remaining height
private fun blitSlicedVertical(
@@ -6,13 +6,17 @@
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.gui.pokedex.widgets.evolutiontree
package com.cobblemon.mod.common.client.gui.pokedex.widgets
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.gui.*
import com.cobblemon.mod.common.api.pokedex.Dexes
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.api.pokedex.entry.CobblemonBiomeDexEvolutionRequirementDisplay
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.DexEvolutionDisplay
import com.cobblemon.mod.common.api.pokedex.entry.DexEvolutionRequirementDisplay
import com.cobblemon.mod.common.api.pokedex.entry.PokedexEntry
import com.cobblemon.mod.common.api.pokedex.entry.PokedexForm
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
@@ -20,6 +24,8 @@ import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.bold
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.CobblemonResources
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUI
@@ -29,18 +35,23 @@ import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.POKEMON_D
import com.cobblemon.mod.common.client.gui.pokedex.PokedexGUIConstants.SCROLL_SLOT_SIZE
import com.cobblemon.mod.common.client.gui.pokedex.ScaledButton
import com.cobblemon.mod.common.client.gui.pokedex.renderTooltip
import com.cobblemon.mod.common.client.gui.pokedex.widgets.PokemonSlotWidget
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.renderScaledGuiItemIcon
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Gender
import com.cobblemon.mod.common.pokemon.RenderablePokemon
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
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.core.registries.Registries
import net.minecraft.locale.Language
import net.minecraft.resources.ResourceLocation
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.tags.TagKey
class EvolutionTreeWidget(
val parent: PokedexGUI,
@@ -92,6 +103,8 @@ class EvolutionTreeWidget(
private var hoveredTooltips: List<MutableComponent>? = null
private var viewedForm: FormData? = null
private val expandButton: ScaledButton = ScaledButton(
0f,
0f,
@@ -100,11 +113,14 @@ class EvolutionTreeWidget(
resource = expandResource,
clickAction = {
parent.evolutionTreeExpanded = !parent.evolutionTreeExpanded
expandButton.resource = if (parent.evolutionTreeExpanded) minimizeResource else expandResource
updatePositioning()
}
).apply { addWidget(this) }
init {
updatePositioning()
}
override fun renderWidget(
guiGraphics: GuiGraphics,
mouseX: Int,
@@ -146,9 +162,33 @@ class EvolutionTreeWidget(
if (pokedexEntry == null || pokedexForm == null) return
updatePositioning()
var rootSpecies = pokedexEntry
var rootForm = pokedexForm
var preEvolution = findPreEvolution(rootSpecies, rootForm)
// Stop at whatever the dex can't resolve, and at cycles (datapacks)
val visited = mutableSetOf(rootSpecies.speciesId to rootForm.displayForm)
while (preEvolution != null) {
val preEvolutionSpecies = Dexes.findDexEntryBySpeciesId(preEvolution.fromSpecies) ?: break
val preEvolutionForm = preEvolutionSpecies.forms.firstOrNull { it.displayForm.equals(preEvolution.fromForm, ignoreCase = true) } ?: break
if (!visited.add(preEvolutionSpecies.speciesId to preEvolutionForm.displayForm)) break
rootSpecies = preEvolutionSpecies
rootForm = preEvolutionForm
preEvolution = findPreEvolution(rootSpecies, rootForm)
}
val (rootPokedexEntry, rootPokedexForm) = Dexes.findRootDexEntryBySpeciesId(pokedexEntry.speciesId, pokedexForm.displayForm)
val rootEvolutionNode = buildEvolutionTree(rootPokedexEntry, rootPokedexForm, null)
val entry = Dexes.findDexEntryBySpeciesId(rootSpecies.speciesId)
?: throw RuntimeException("Root dex entry not found")
val viewedFormEquivalents = Dexes.getFormEquivalents(pokedexForm.displayForm)
viewedForm = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId)
?.forms?.firstOrNull { viewedFormEquivalents.contains(it.name.lowercase()) }
val formEquivalents = Dexes.getFormEquivalents(rootForm.displayForm)
val form =
entry.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) } ?:
entry.forms.first { it.displayForm.equals("Normal", ignoreCase = true) }
val rootEvolutionNode = buildEvolutionTree(entry, form)
// val isSameEntry = previousRootPokedexEntry != null && pokedexEntry?.id == previousRootPokedexEntry?.id
// val isSameForm =
@@ -172,6 +212,13 @@ class EvolutionTreeWidget(
panningY = 0.0
}
/** The evolution leading to [form]; the standard form falls back to any form of the species (no evolution leads to regular Vivillon). */
private fun findPreEvolution(entry: PokedexEntry, form: PokedexForm): DexEvolutionDisplay? {
return DexEntries.evolutionDisplays.firstOrNull { it.toSpecies == entry.speciesId && it.toForm == form.displayForm }
?: DexEntries.evolutionDisplays.takeIf { form.displayForm.equals("Normal", ignoreCase = true) }
?.firstOrNull { it.toSpecies == entry.speciesId }
}
fun updatePositioning() {
x = pX + X_MARGIN
y = pY + if (parent.evolutionTreeExpanded) Y_MARGIN_EXPANDED else Y_MARGIN_MINIMIZED
@@ -180,6 +227,7 @@ class EvolutionTreeWidget(
panningX = 0.0
panningY = 0.0
expandButton.resource = if (parent.evolutionTreeExpanded) minimizeResource else expandResource
expandButton.buttonX = x + EXPAND_BUTTON_X_MARGIN
expandButton.buttonY = y + EXPAND_BUTTON_Y_MARGIN
}
@@ -187,61 +235,61 @@ class EvolutionTreeWidget(
private fun buildEvolutionTree(
pokedexEntry: PokedexEntry,
pokedexForm: PokedexForm,
evolution: Evolution? = null,
resultProperties: PokemonProperties? = evolution?.result,
renderData: RenderEvolutionData? = evolution?.let { renderEvolutionData(it) }
aspects: Set<String> = emptySet(),
ancestors: Set<Pair<ResourceLocation, String>> = emptySet(),
): EvolutionNode {
val species = PokemonSpecies.getByIdentifier(pokedexEntry.speciesId)!!
// Datapack evolutions can loop back (A -> B -> A), so stop at anything already on this branch
val path = ancestors + (pokedexEntry.speciesId to pokedexForm.displayForm.lowercase())
val discoveryLevel = CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry, pokedexForm, aspects.toMutableSet())
val formEquivalents = Dexes.getFormEquivalents(pokedexForm.displayForm)
val form = species.forms.firstOrNull { formEquivalents.contains(it.name.lowercase()) }
val parentSpecies = Dexes.findDexEntryBySpeciesId(species.resourceIdentifier)!!
val parentPokedexForm = parentSpecies.forms.first { formEquivalents.contains(it.displayForm.lowercase()) }
val evolutions = DexEntries.evolutionDisplays.filter { it.fromSpecies == pokedexEntry.speciesId && formEquivalents.contains(it.fromForm.lowercase()) }
val aspects = resultProperties?.aspects?.toMutableSet() ?: mutableSetOf()
form?.aspects?.let { aspects.addAll(it) }
val discoveryLevel = CobblemonClient.clientPokedexData.getHighestKnowledgeFor(pokedexEntry, pokedexForm, aspects)
val evolutions = form?.evolutions ?: species.evolutions
// Avoid evolution.result.create() here: it builds a full battle-ready Pokemon (initializeMovesetFromDefault
// etc.) which relies on server-only data like CobblemonMovesetBuilders and crashes on the client
val children = evolutions.flatMap { evolution ->
val baseRenderData = renderEvolutionData(evolution)
listOfNotNull(evolution.result, evolution.shedder).mapNotNull { resultProperties ->
val resultSpecies = resultProperties.species
?.let { PokemonSpecies.getByIdentifier(it.asIdentifierDefaultingNamespace()) }
?: return@mapNotNull null
val resultForm = resultSpecies.getForm(aspects + resultProperties.aspects)
val speciesId = resultSpecies.resourceIdentifier
val childPokedexEntry = Dexes.findDexEntryBySpeciesId(speciesId)
if (childPokedexEntry == null) {
Cobblemon.LOGGER.warn("Dex entry not found for species: $speciesId")
return@mapNotNull null
}
val formEquivalents = Dexes.getFormEquivalents(resultForm.name.lowercase())
val childPokedexForm = childPokedexEntry.forms.first { formEquivalents.contains(it.displayForm.lowercase()) }
val childRenderData = if (resultProperties === evolution.shedder) {
RenderEvolutionData(
icons = (baseRenderData?.icons ?: emptyList()) + CobblemonItems.POKE_BALL,
tooltips = (baseRenderData?.tooltips ?: emptyList()) + lang("ui.pokedex.evolution.shedder.tooltip"),
)
} else {
baseRenderData
}
return@mapNotNull buildEvolutionTree(childPokedexEntry, childPokedexForm, evolution, resultProperties, childRenderData)
val children = evolutions.mapNotNull {
val childPokedexEntry = Dexes.findDexEntryBySpeciesId(it.toSpecies)
if (childPokedexEntry == null) {
Cobblemon.LOGGER.warn("Dex entry not found for species: ${it.toSpecies}")
return@mapNotNull null
}
val formEquivalents = Dexes.getFormEquivalents(it.toForm.lowercase())
val childPokedexForm = childPokedexEntry.forms.firstOrNull { formEquivalents.contains(it.displayForm.lowercase()) }
if (childPokedexForm == null) {
Cobblemon.LOGGER.warn("Dex form ${it.toForm} not found for species: ${it.toSpecies}")
return@mapNotNull null
}
if ((childPokedexEntry.speciesId to childPokedexForm.displayForm.lowercase()) in path) {
Cobblemon.LOGGER.warn("Evolution loop found at species: ${it.toSpecies} form: ${it.toForm}")
return@mapNotNull null
}
buildEvolutionTree(childPokedexEntry, childPokedexForm, it.toAspects, path)
}
return EvolutionNode(pokedexEntry, parentPokedexForm, aspects, discoveryLevel, children, renderData)
return EvolutionNode(
pokedexEntry = pokedexEntry,
pokedexForm = pokedexForm,
aspects = aspects,
pokedexEntryProgress = discoveryLevel,
children = mergeEquivalentChildren(children),
renderDataAlternatives = evolutions,
)
}
private fun mergeEquivalentChildren(children: List<EvolutionNode>): List<EvolutionNode> {
return children
.groupBy { Triple(it.pokedexEntry.speciesId, it.pokedexForm.displayForm.lowercase(), it.aspects) }
.values
.map { group ->
if (group.size == 1) return@map group.first()
val first = group.first()
EvolutionNode(
first.pokedexEntry,
first.pokedexForm,
first.aspects,
first.pokedexEntryProgress,
first.children,
group.flatMap { it.renderDataAlternatives }
)
}
}
private fun renderTreeDrawingNode(
@@ -258,15 +306,19 @@ class EvolutionTreeWidget(
val (childX, _) = getDrawingCoordinates(child)
val lineStartX = treeX + SCROLL_SLOT_SIZE
val lineEndX = childX
val lineY = treeY + SCROLL_SLOT_SIZE / 2
child.value.renderData?.let { renderData ->
val formEquivalents = Dexes.getFormEquivalents(child.value.pokedexForm.displayForm)
val alternatives = treeDrawingNode.value.renderDataAlternatives.filter {
it.toSpecies == child.value.pokedexEntry.speciesId && formEquivalents.contains(it.toForm.lowercase()) && it.toAspects == child.value.aspects
}
alternatives.takeIf { it.isNotEmpty() }?.let {
drawEvolutionInfo(
guiGraphics,
renderData,
it,
lineStartX,
lineEndX,
childX,
lineY,
mouseX,
mouseY,
@@ -298,16 +350,19 @@ class EvolutionTreeWidget(
treeDrawingNode.children.forEach { child ->
val (childX, childY) = getDrawingCoordinates(child)
val lineStartX = treeForkEnd
val lineEndX = childX
val lineY = childY + SCROLL_SLOT_SIZE / 2
child.value.renderData?.let { renderData ->
val formEquivalents = Dexes.getFormEquivalents(child.value.pokedexForm.displayForm)
val alternatives = treeDrawingNode.value.renderDataAlternatives.filter {
it.toSpecies == child.value.pokedexEntry.speciesId && formEquivalents.contains(it.toForm.lowercase()) && it.toAspects == child.value.aspects
}
alternatives.takeIf { it.isNotEmpty() }?.let {
drawEvolutionInfo(
guiGraphics,
renderData,
lineStartX,
lineEndX,
it,
treeForkEnd,
childX,
lineY,
mouseX,
mouseY,
@@ -346,7 +401,7 @@ class EvolutionTreeWidget(
if (formName == "normal") null
else lang("ui.pokedex.info.form.${species}-${formName.replace("-", "")}")
formComponent?.add(" ")?.add(speciesComponent) ?: speciesComponent
formComponent?.let { speciesComponent.copy().add(" (").add(it).add(")") } ?: speciesComponent
}
guiGraphics.disableScissor()
@@ -408,7 +463,7 @@ class EvolutionTreeWidget(
private fun drawEvolutionInfo(
guiGraphics: GuiGraphics,
renderData: RenderEvolutionData,
renderDataAlternatives: List<DexEvolutionDisplay>,
lineStartX: Int,
lineEndX: Int,
lineY: Int,
@@ -416,6 +471,26 @@ class EvolutionTreeWidget(
mouseY: Int,
delta: Float,
) {
val icons = renderDataAlternatives.flatMap { it.requirements.filter(::isDisplayable).map { req -> req.icon } }.distinct()
val playerBiome = Minecraft.getInstance().player?.let { it.level().getBiome(it.blockPosition()) }
val alternativeTooltips = renderDataAlternatives.map { alternative ->
alternative.requirements.filter(::isDisplayable).map { req ->
if (req is CobblemonBiomeDexEvolutionRequirementDisplay) {
val tooltip = req.tooltip.copy()
if (playerBiome != null) {
val conditionFits = req.condition?.let { if (it.tag) playerBiome.`is`(TagKey.create(Registries.BIOME, it.id)) else playerBiome.`is`(it.id) } ?: true
val antiConditionFits = req.antiCondition?.let { if (it.tag) playerBiome.`is`(TagKey.create(Registries.BIOME, it.id)) else playerBiome.`is`(it.id) } ?: false
val isMet = conditionFits && !antiConditionFits
tooltip.add(if (isMet) Component.literal(" ✔").green() else Component.literal(" ✘").red())
}
tooltip
} else {
req.tooltip
}
}
}
val tooltips = combineAlternativeTooltips(alternativeTooltips)
val lineXMiddle = (lineStartX + lineEndX) / 2
val iconX = (lineXMiddle + ICON_X_PADDING - ICON_SLOT_HALF_PADDING).toInt()
val iconY = (lineY + ICON_Y_PADDING - ICON_SLOT_HALF_PADDING).toInt()
@@ -431,7 +506,7 @@ class EvolutionTreeWidget(
return
}
if (renderData.icons.isEmpty()) {
if (icons.isEmpty()) {
guiGraphics.hLine(
lineStartX,
lineEndX,
@@ -462,8 +537,8 @@ class EvolutionTreeWidget(
height = iconSize,
)
val index = (System.currentTimeMillis() / MILLIS_BETWEEN_ICONS_CYCLE) % renderData.icons.size
val item = renderData.icons[index.toInt()]
val index = (System.currentTimeMillis() / MILLIS_BETWEEN_ICONS_CYCLE) % icons.size
val item = icons[index.toInt()]
renderScaledGuiItemIcon(
itemStack = item.defaultInstance,
@@ -478,17 +553,49 @@ class EvolutionTreeWidget(
val isIconHovered = isElementHovered(
mouseX,
mouseY,
iconX.toInt(),
iconX.toInt() + iconSize,
iconY.toInt(),
iconY.toInt() + iconSize,
iconX,
iconX + iconSize,
iconY,
iconY + iconSize,
)
if (renderData.tooltips.isNotEmpty() &&
if (tooltips.isNotEmpty() &&
(isLineHovered || isIconHovered) &&
this.isHovered(mouseX.toDouble(), mouseY.toDouble())
) {
hoveredTooltips = renderData.tooltips
hoveredTooltips = tooltips.map { it.copy() }
}
}
/** Hides biome requirements naming untranslated biomes, e.g. from mods that aren't installed (aether:skyroot_forest) */
private fun isDisplayable(requirement: DexEvolutionRequirementDisplay): Boolean {
if (requirement !is CobblemonBiomeDexEvolutionRequirementDisplay) return true
return listOfNotNull(requirement.condition, requirement.antiCondition).all {
it.tag || Language.getInstance().has("biome.${it.id.namespace}.${it.id.path}")
}
}
private fun combineAlternativeTooltips(alternatives: List<List<Component>>): List<Component> {
val groups = alternatives.filter { it.isNotEmpty() }
if (groups.size <= 1) return groups.firstOrNull() ?: emptyList()
val sharedStrings = groups.map { group -> group.map { it.string }.toSet() }.reduce { acc, strings -> acc intersect strings }
val shared = groups.first().filter { it.string in sharedStrings }.distinctBy { it.string }
val remainders = groups
.map { group -> group.filter { it.string !in sharedStrings } }
.distinctBy { group -> group.map { it.string } }
// An alternative needing nothing beyond the shared lines makes the others irrelevant
if (remainders.any { it.isEmpty() }) return shared
if (remainders.size == 1) return shared + remainders.first()
return if (remainders.size >= 3 && remainders.all { it.size == 1 }) {
shared + lang("ui.pokedex.evolution.one_of").bold() + remainders.map { it.first() }
} else {
// Show each alternative in full, otherwise it's unclear where the first one starts
groups.distinctBy { group -> group.map { it.string } }.flatMapIndexed { index, group ->
if (index == 0) group else listOf(lang("ui.pokedex.evolution.or").bold()) + group
}
}
}
@@ -566,6 +673,6 @@ class EvolutionTreeWidget(
val aspects: Set<String>,
val pokedexEntryProgress: PokedexEntryProgress,
override val children: List<EvolutionNode>,
val renderData: RenderEvolutionData?,
val renderDataAlternatives: List<DexEvolutionDisplay>,
) : TreeNode<EvolutionNode>
}
@@ -1,48 +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.pokedex.widgets.evolutiontree
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
fun debugEvolutionRequirementRendering() {
if (!Cobblemon.config.enableDebugKeys) {
return
}
val gaps = mutableListOf<Pair<String, String>>()
for (species in PokemonSpecies.implemented) {
val evolutions = species.evolutions + species.forms.flatMap { it.evolutions }
for (evolution in evolutions) {
if (renderEvolutionData(evolution) == null) {
gaps.add("Evolution: ${evolution::class.simpleName}" to species.showdownId())
}
for (requirement in evolution.requirements) {
if (resolveRequirementData(requirement) == null) {
gaps.add("Requirement: ${requirement::class.simpleName}" to species.showdownId())
}
}
}
}
val player = Minecraft.getInstance().player
if (gaps.isEmpty()) {
player?.sendSystemMessage(Component.literal("Evolution render debug: no gaps found."))
return
}
val byType = gaps.groupBy({ it.first }, { it.second })
println("Evolution render debug found ${gaps.size} gap(s) across ${byType.size} type(s):")
byType.forEach { (type, species) -> println(" $type: ${species.distinct().joinToString(", ")}") }
player?.sendSystemMessage(Component.literal("Evolution render debug: ${gaps.size} gap(s) across ${byType.size} type(s), see log for details."))
}
@@ -1,123 +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.pokedex.widgets.evolutiontree
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.pokemon.evolution.variants.BlockClickEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.DummyEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.LevelUpEvolution
import com.cobblemon.mod.common.pokemon.evolution.variants.TradeEvolution
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.itemRegistry
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.Item
data class RenderEvolutionData(
val icons: List<Item>,
val tooltips: List<MutableComponent>,
)
internal fun resolveRequirementData(requirement: Requirement): RenderRequirementData? {
val icon = requirement.pokedexIcon?.let {
Minecraft.getInstance()?.player?.level()?.itemRegistry?.get(it)
}
val tooltip = requirement.pokedexTooltip?.let {
Component.translatable(it)
}
if (icon != null && tooltip != null) {
return RenderRequirementData(icon, tooltip)
}
return renderRequirementData(requirement)
}
fun renderEvolutionData(evolution: Evolution): RenderEvolutionData? {
when (evolution) {
is BlockClickEvolution -> return renderBlockClickEvolution(evolution)
is DummyEvolution -> return renderDummyEvolution(evolution)
is ItemInteractionEvolution -> return renderItemInteractionEvolution(evolution)
is LevelUpEvolution -> return renderLevelUpEvolution(evolution)
is TradeEvolution -> return renderTradeEvolution(evolution)
}
return null
}
fun renderBlockClickEvolution(blockClickEvolution: BlockClickEvolution): RenderEvolutionData? {
return null
}
// TODO: Use dummyEvolution?
fun renderDummyEvolution(evolution: Evolution): RenderEvolutionData? {
return null
}
fun renderItemInteractionEvolution(itemInteractionEvolution: ItemInteractionEvolution): RenderEvolutionData? {
val item = itemInteractionEvolution.requiredContext.items.get().first().value()
val icons = mutableListOf(item)
val tooltips =
mutableListOf(lang("ui.pokedex.evolution.item_interact.tooltip", item.description.plainCopy().green()))
val requirementsDataList = itemInteractionEvolution.requirements.mapNotNull { requirement ->
resolveRequirementData(requirement)
}
requirementsDataList.forEach { requirement ->
icons.add(requirement.icon)
tooltips.add(requirement.tooltip)
}
return RenderEvolutionData(icons, tooltips)
}
fun renderLevelUpEvolution(levelUpEvolution: LevelUpEvolution): RenderEvolutionData? {
val requirementsDataList = levelUpEvolution.requirements.map { requirement ->
resolveRequirementData(requirement)
}
val icons = requirementsDataList.mapNotNull { requirementData -> requirementData?.icon }
val tooltips = requirementsDataList.mapNotNull { requirementData -> requirementData?.tooltip }
return RenderEvolutionData(icons, tooltips)
}
fun renderTradeEvolution(tradeEvolution: TradeEvolution): RenderEvolutionData? {
val icons = mutableListOf<Item>(CobblemonItems.LINK_CABLE)
val tooltips = mutableListOf<MutableComponent>()
val species = tradeEvolution.requiredContext.species
if (species == null) {
tooltips.add(lang("ui.pokedex.evolution.trade.tooltip.with_any_species"))
} else {
PokemonSpecies.getByIdentifier(species.asIdentifierDefaultingNamespace())?.let {
tooltips.add(lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()))
}
}
val requirementsDataList = tradeEvolution.requirements.mapNotNull { requirement ->
resolveRequirementData(requirement)
}
requirementsDataList.forEach { requirement ->
icons.add(requirement.icon)
tooltips.add(requirement.tooltip)
}
return RenderEvolutionData(icons, tooltips)
}
@@ -1,379 +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.pokedex.widgets.evolutiontree
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.conditional.RegistryLikeCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeIdentifierCondition
import com.cobblemon.mod.common.api.conditional.RegistryLikeTagCondition
import com.cobblemon.mod.common.api.pokemon.feature.IntSpeciesFeatureProvider
import com.cobblemon.mod.common.api.pokemon.feature.SpeciesFeatures
import com.cobblemon.mod.common.api.pokemon.requirement.Requirement
import com.cobblemon.mod.common.api.pokemon.stats.Stats
import com.cobblemon.mod.common.api.text.add
import com.cobblemon.mod.common.api.text.green
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.pokemon.requirements.*
import com.cobblemon.mod.common.util.asTranslated
import com.cobblemon.mod.common.util.lang
import net.minecraft.client.Minecraft
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.chat.Component
import net.minecraft.network.chat.MutableComponent
import net.minecraft.world.item.Item
import net.minecraft.world.item.Items
import net.minecraft.world.level.biome.Biome
import net.minecraft.world.level.levelgen.structure.Structure
data class RenderRequirementData(
val icon: Item,
val tooltip: MutableComponent,
)
fun renderRequirementData(requirement: Requirement): RenderRequirementData? {
when (requirement) {
is AdvancementRequirement -> return renderAdvancementRequirement(requirement)
is AnyRequirement -> return renderAnyRequirement(requirement)
is AreaRequirement -> return renderAreaRequirement(requirement)
is AttackDefenceRatioRequirement -> return renderAttackDefenceRatioRequirement(requirement)
is BattleCriticalHitsRequirement -> return renderBattleCriticalHitsRequirement(requirement)
is BiomeRequirement -> return renderBiomeRequirement(requirement)
is BlocksTraveledRequirement -> return renderBlocksTraveledRequirement(requirement)
is DamageTakenRequirement -> return renderDamageTakenRequirement(requirement)
is DefeatRequirement -> return renderDefeatRequirement(requirement)
is FriendshipRequirement -> return renderFriendshipRequirement(requirement)
is HeldItemRequirement -> return renderHeldItemRequirement(requirement)
is LevelRequirement -> return renderLevelRequirement(requirement)
is MoonPhaseRequirement -> return renderMoonPhaseRequirement(requirement)
is MoveSetRequirement -> return renderMoveSetRequirement(requirement)
is MoveTypeRequirement -> return renderMoveTypeRequirement(requirement)
is OwnerHoldsItemRequirement -> return renderOwnerHoldsItemRequirement(requirement)
is PartyMemberRequirement -> return renderPartyMemberRequirement(requirement)
is PokemonPropertiesRequirement -> return renderPokemonPropertiesRequirement(requirement)
is PropertyRangeRequirement -> return renderPropertyRangeRequirement(requirement)
is RecoilRequirement -> return renderRecoilRequirement(requirement)
is StatCompareRequirement -> return renderStatCompareRequirement(requirement)
is StatEqualRequirement -> return renderStatEqualRequirement(requirement)
is StructureRequirement -> return renderStructureRequirement(requirement)
is TimeRangeRequirement -> return renderTimeRangeRequirement(requirement)
is UseMoveRequirement -> return renderUseMoveRequirement(requirement)
is WeatherRequirement -> return renderWeatherRequirement(requirement)
is WorldRequirement -> return renderWorldRequirement(requirement)
}
return null
}
fun renderAdvancementRequirement(advancementRequirement: AdvancementRequirement): RenderRequirementData? {
val icon = Items.BOOK
val identifier = advancementRequirement.requiredAdvancement
val advancementLanguageKey = "${identifier.namespace}.${identifier.path.substringAfterLast('/')}"
val advancementComponent = Component.translatable("advancements.$advancementLanguageKey").green()
val tooltip = lang("ui.pokedex.requirement.advancement.tooltip", advancementComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderAnyRequirement(anyRequirement: AnyRequirement): RenderRequirementData? {
return null
}
fun renderAreaRequirement(areaRequirement: AreaRequirement): RenderRequirementData? {
return null
}
fun renderAttackDefenceRatioRequirement(attackDefenceRatioRequirement: AttackDefenceRatioRequirement): RenderRequirementData? {
return null
}
fun renderBattleCriticalHitsRequirement(battleCriticalHitsRequirement: BattleCriticalHitsRequirement): RenderRequirementData? {
val icon = CobblemonItems.MEDICINAL_LEEK
val criticalsComponent = Component.literal(battleCriticalHitsRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.battle_critical_hits.tooltip", criticalsComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderBiomeRequirement(biomeRequirement: BiomeRequirement): RenderRequirementData? {
val condition = biomeRequirement.biomeCondition
val anticondition = biomeRequirement.biomeAnticondition
if (condition == null && anticondition == null) {
return null
}
val icon = Items.COMPASS
val displayName: (RegistryLikeCondition<Biome>) -> MutableComponent = {
when (it) {
is RegistryLikeTagCondition -> Component.literal(it.tag.location().path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) })
is RegistryLikeIdentifierCondition -> Component.translatable("biome.${it.identifier.namespace}.${it.identifier.path}")
else -> Component.literal(it.toString())
}
}
val tooltip = if (condition != null && anticondition != null) {
lang("ui.pokedex.requirement.biome.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.biome.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.biome.anticondition.tooltip", displayName(anticondition!!).green())
}
val playerBiome = Minecraft.getInstance()?.player?.let { it.level().getBiome(it.blockPosition()) }
if (playerBiome != null) {
val isMet = (condition == null || condition.fits(playerBiome)) && (anticondition == null || !anticondition.fits(playerBiome))
tooltip.add(if (isMet) Component.literal(" ✔").green() else Component.literal(" ✘").red())
}
return RenderRequirementData(icon, tooltip)
}
fun renderBlocksTraveledRequirement(blocksTraveledRequirement: BlocksTraveledRequirement): RenderRequirementData? {
val icon = Items.LEATHER_BOOTS
val blocksComponent = Component.literal(blocksTraveledRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.blocks_traveled.tooltip", blocksComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderDamageTakenRequirement(damageTakenRequirement: DamageTakenRequirement): RenderRequirementData? {
val icon = CobblemonItems.PUNCHING_GLOVE
val blocksComponent = Component.literal(damageTakenRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.damage_taken.tooltip", blocksComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderDefeatRequirement(defeatRequirement: DefeatRequirement): RenderRequirementData? {
val description = describePokemonProperties(defeatRequirement.target) ?: return null
val icon = CobblemonItems.PUNCHING_GLOVE
val defeatedAmount = defeatRequirement.amount
val descriptionComponent = Component.literal(defeatedAmount.toString()).green().add(" ").add(description)
val tooltip = lang("ui.pokedex.requirement.defeat.tooltip", descriptionComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderFriendshipRequirement(friendshipRequirement: FriendshipRequirement): RenderRequirementData? {
val icon = CobblemonItems.SOOTHE_BELL
val friendshipAmountComponent = Component.literal(friendshipRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.friendship.tooltip", friendshipAmountComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderHeldItemRequirement(heldItemRequirement: HeldItemRequirement): RenderRequirementData? {
val item = heldItemRequirement.itemCondition.items.get().first().value()
val tooltip = lang("ui.pokedex.requirement.held_item.tooltip", item.description.plainCopy().green())
return RenderRequirementData(item, tooltip)
}
fun renderLevelRequirement(levelRequirement: LevelRequirement): RenderRequirementData? {
val minLevel = levelRequirement.minLevel
val maxLevel = levelRequirement.maxLevel
val icon = CobblemonItems.RARE_CANDY
val tooltip =
if (minLevel == maxLevel || maxLevel == Int.MAX_VALUE)
lang("ui.pokedex.requirement.level.tooltip.min", Component.literal(minLevel.toString()).green())
else
lang("ui.pokedex.requirement.level.tooltip.between", Component.literal(minLevel.toString()).green(), Component.literal(maxLevel.toString()).green())
return RenderRequirementData(icon, tooltip)
}
fun renderMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): RenderRequirementData? {
val icon = Items.CLOCK
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderMoveSetRequirement(moveSetRequirement: MoveSetRequirement): RenderRequirementData? {
val icon = CobblemonItems.TECHNICAL_MACHINE
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
return RenderRequirementData(icon, tooltip)
}
fun renderMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): RenderRequirementData? {
val type = ElementalTypes.get(moveTypeRequirement.type.name)
val icon = when (type) {
ElementalTypes.NORMAL -> CobblemonItems.NORMAL_GEM
ElementalTypes.FIRE -> CobblemonItems.FIRE_GEM
ElementalTypes.WATER -> CobblemonItems.WATER_GEM
ElementalTypes.GRASS -> CobblemonItems.GRASS_GEM
ElementalTypes.ELECTRIC -> CobblemonItems.ELECTRIC_GEM
ElementalTypes.ICE -> CobblemonItems.ICE_GEM
ElementalTypes.FIGHTING -> CobblemonItems.FIGHTING_GEM
ElementalTypes.POISON -> CobblemonItems.POISON_GEM
ElementalTypes.GROUND -> CobblemonItems.GROUND_GEM
ElementalTypes.FLYING -> CobblemonItems.FLYING_GEM
ElementalTypes.PSYCHIC -> CobblemonItems.PSYCHIC_GEM
ElementalTypes.BUG -> CobblemonItems.BUG_GEM
ElementalTypes.ROCK -> CobblemonItems.ROCK_GEM
ElementalTypes.GHOST -> CobblemonItems.GHOST_GEM
ElementalTypes.DRAGON -> CobblemonItems.DRAGON_GEM
ElementalTypes.DARK -> CobblemonItems.DARK_GEM
ElementalTypes.STEEL -> CobblemonItems.STEEL_GEM
ElementalTypes.FAIRY -> CobblemonItems.FAIRY_GEM
else -> CobblemonItems.NORMAL_GEM
}
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
return RenderRequirementData(icon, tooltip)
}
fun renderOwnerHoldsItemRequirement(ownerHoldsItemRequirement: OwnerHoldsItemRequirement): RenderRequirementData? {
val item = ownerHoldsItemRequirement.itemCondition.items.get().first().value()
val tooltip = lang("ui.pokedex.requirement.owner_held_item.tooltip", item.description.plainCopy().green())
return RenderRequirementData(item, tooltip)
}
fun renderPartyMemberRequirement(partyMemberRequirement: PartyMemberRequirement): RenderRequirementData? {
val description = describePokemonProperties(partyMemberRequirement.target) ?: return null
val icon = CobblemonItems.POKE_BALL
val tooltip = if (partyMemberRequirement.contains) {
lang("ui.pokedex.requirement.party_member.tooltip", description)
} else {
lang("ui.pokedex.requirement.party_member.anticondition.tooltip", description)
}
return RenderRequirementData(icon, tooltip)
}
fun renderPokemonPropertiesRequirement(pokemonPropertiesRequirement: PokemonPropertiesRequirement): RenderRequirementData? {
val description = describePokemonProperties(pokemonPropertiesRequirement.target) ?: return null
val icon = Items.NAME_TAG
val tooltip = lang("ui.pokedex.requirement.pokemon_properties.tooltip", description)
return RenderRequirementData(icon, tooltip)
}
fun renderPropertyRangeRequirement(propertyRangeRequirement: PropertyRangeRequirement): RenderRequirementData? {
val provider = SpeciesFeatures.getFeature(propertyRangeRequirement.feature) as? IntSpeciesFeatureProvider ?: return null
val displayName = provider.display?.name?.takeIf { it.isNotBlank() }?.asTranslated()?.green() ?: return null
val icon = provider.itemPoints.keys.firstOrNull()?.let { BuiltInRegistries.ITEM.get(it) } ?: Items.NAME_TAG
val range = propertyRangeRequirement.range
val tooltip = lang(
"ui.pokedex.requirement.property_range.tooltip",
displayName,
Component.literal(range.first.toString()).green(),
Component.literal(range.last.toString()).green()
)
return RenderRequirementData(icon, tooltip)
}
fun renderRecoilRequirement(recoilRequirement: RecoilRequirement): RenderRequirementData? {
val icon = CobblemonItems.LIFE_ORB
val recoilComponent = Component.literal(recoilRequirement.amount.toString()).green()
val tooltip = lang("ui.pokedex.requirement.recoil.tooltip", recoilComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderStatCompareRequirement(statCompareRequirement: StatCompareRequirement): RenderRequirementData? {
val highStat = Stats.getStat(statCompareRequirement.highStat)
val lowStat = Stats.getStat(statCompareRequirement.lowStat)
val icon = when (highStat) {
Stats.HP, Stats.EVASION, Stats.ACCURACY -> CobblemonItems.HEALTH_MOCHI
Stats.ATTACK -> CobblemonItems.MUSCLE_MOCHI
Stats.DEFENCE -> CobblemonItems.RESIST_MOCHI
Stats.SPECIAL_ATTACK -> CobblemonItems.GENIUS_MOCHI
Stats.SPECIAL_DEFENCE -> CobblemonItems.CLEVER_MOCHI
Stats.SPEED -> CobblemonItems.SWIFT_MOCHI
}
val highStatComponent = highStat.displayName.plainCopy().green()
val lowStatComponent = lowStat.displayName.plainCopy().green()
val tooltip = lang("ui.pokedex.requirement.stat_compare.tooltip", highStatComponent, lowStatComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderStatEqualRequirement(statEqualRequirement: StatEqualRequirement): RenderRequirementData? {
val statOne = Stats.getStat(statEqualRequirement.statOne)
val statTwo = Stats.getStat(statEqualRequirement.statTwo)
val statOneComponent = statOne.displayName.plainCopy().green()
val statTwoComponent = statTwo.displayName.plainCopy().green()
val icon = CobblemonItems.FRESH_START_MOCHI
val tooltip = lang("ui.pokedex.requirement.stat_equal.tooltip", statOneComponent, statTwoComponent)
return RenderRequirementData(icon, tooltip)
}
fun renderStructureRequirement(structureRequirement: StructureRequirement): RenderRequirementData? {
val condition = structureRequirement.structureCondition
val anticondition = structureRequirement.structureAnticondition
if (condition == null && anticondition == null) {
return null
}
val icon = Items.STRUCTURE_BLOCK
val displayName: (RegistryLikeCondition<Structure>) -> MutableComponent = {
val path = when (it) {
is RegistryLikeTagCondition -> it.tag.location().path
is RegistryLikeIdentifierCondition -> it.identifier.path
else -> it.toString()
}
Component.literal(path.substringAfterLast('/').split('_').joinToString(" ") { part -> part.replaceFirstChar(Char::titlecase) })
}
val tooltip = if (condition != null && anticondition != null) {
lang("ui.pokedex.requirement.structure.both.tooltip", displayName(condition).green(), displayName(anticondition).green())
} else if (condition != null) {
lang("ui.pokedex.requirement.structure.tooltip", displayName(condition).green())
} else {
lang("ui.pokedex.requirement.structure.anticondition.tooltip", displayName(anticondition!!).green())
}
return RenderRequirementData(icon, tooltip)
}
fun renderTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): RenderRequirementData? {
val icon = Items.CLOCK
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
return RenderRequirementData(icon, tooltip)
}
fun renderUseMoveRequirement(useMoveRequirement: UseMoveRequirement): RenderRequirementData? {
val icon = CobblemonItems.CHOICE_BAND
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
return RenderRequirementData(icon, tooltip)
}
fun renderWeatherRequirement(weatherRequirement: WeatherRequirement): RenderRequirementData? {
val icon: Item
val tooltip: MutableComponent
if (weatherRequirement.isThundering == true) {
icon = Items.LIGHTNING_ROD
tooltip = lang("ui.pokedex.requirement.weather.tooltip.thundering")
} else {
icon = Items.WATER_BUCKET
tooltip = lang("ui.pokedex.requirement.weather.tooltip.raining")
}
return RenderRequirementData(icon, tooltip)
}
fun renderWorldRequirement(worldRequirement: WorldRequirement): RenderRequirementData? {
return null
}
@@ -20,7 +20,7 @@ object BattleCaptureStartHandler : ClientNetworkPacketHandler<BattleCaptureStart
val battle = CobblemonClient.battle ?: return
val targetPokemon = battle.getPokemonFromPNX(packet.targetPNX)
val tile = targetPokemon.second
val pokeBall = PokeBalls.getPokeBall(packet.pokeBallType) ?: PokeBalls.POKE_BALL
val pokeBall = PokeBalls.getPokeBall(packet.pokeBallType)
tile.ballCapturing = ClientBallDisplay(pokeBall, packet.aspects).also { it.start() }
}
}
}
@@ -24,8 +24,9 @@ object OpenPCHandler : ClientNetworkPacketHandler<OpenPCPacket> {
party = CobblemonClient.storage.party,
configuration = PCGUIConfiguration(),
openOnBox = packet.box ?: CobblemonClient.lastPcBoxViewed,
unseenWallpapers = packet.unseenWallpapers.toMutableSet()
unseenWallpapers = packet.unseenWallpapers.toMutableSet(),
silent = packet.silent
)
)
}
}
}
@@ -133,11 +133,11 @@ class PokeBobberEntityRenderer(context: EntityRendererProvider.Context?) : Entit
val pokeBobberBaitItemStack = fishingBobberEntity.entityData.get(PokeRodFishingBobberEntity.POKEBOBBER_BAIT)
val pokeRodId = ResourceLocation.tryParse(pokeRodIdStr)
val pokeRod = PokeRods.getPokeRod(pokeRodId!!)
val ballItem = PokeBalls.getPokeBall(pokeRod?.pokeBallId!!)!!.item
val ballItem = PokeBalls.getPokeBall(pokeRod?.pokeBallId!!)!!.stack
// render the pokebobber
Minecraft.getInstance().itemRenderer.renderStatic(
ballItem.defaultInstance,
ballItem,
ItemDisplayContext.GROUND,
light,
OverlayTexture.NO_OVERLAY,
@@ -0,0 +1,25 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokeball
import com.cobblemon.mod.common.client.render.models.blockbench.PosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.frame.ModelFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import net.minecraft.client.model.geom.ModelPart
open class PokeBallPosableModel(root: Bone) : PosableModel(root), ModelFrame {
@Transient
override val rootPart =
(root as ModelPart)
.children.entries
.first()
.let { root.registerChildWithAllChildren(it.key) }
val rootBone: String = (root as ModelPart).children.entries.first().key
}
@@ -13,11 +13,11 @@ import com.cobblemon.mod.common.client.render.models.blockbench.repository.Rende
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import net.minecraft.world.entity.Entity
class PosablePokeBallModel : PosableEntityModel<EmptyPokeBallEntity>() {
class PosablePokeBallEntityModel : PosableEntityModel<EmptyPokeBallEntity>() {
override fun setupEntityTypeContext(entity: Entity?) {
super.setupEntityTypeContext(entity)
if (entity is EmptyPokeBallEntity) {
context.put(RenderContext.ASPECTS, entity.aspects)
}
}
}
}
@@ -1,98 +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.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CaterpieModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("caterpie")
override val head = getPart("head")
override var portraitScale = 2.1F
override var portraitTranslation = Vec3(-0.55, -1.55, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var shoulderLeft: CobblemonPose
lateinit var shoulderRight: CobblemonPose
val shoulderOffset = 3
override val cryAnimation = CryProvider { bedrockStateful("caterpie", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("caterpie", "blink")}
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
animations = arrayOf(bedrock("caterpie", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("caterpie", "ground_idle")
)
)
walk = registerPose(
poseName = "walking",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("caterpie", "ground_walk")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("caterpie", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("caterpie", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset)
)
)
}
override fun getFaintAnimation(state: PosableState) = bedrockStateful("caterpie", "faint")
}
@@ -1,98 +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.render.models.blockbench.pokemon.gen1
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.MOVING_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.STATIONARY_POSES
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class WeedleModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("weedle")
override val head = getPart("head")
override var portraitScale = 2.2F
override var portraitTranslation = Vec3(-0.31, -1.83, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.1, 0.6, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
val shoulderOffset = 3.8
val shoulderZOffset = -3
override val cryAnimation = CryProvider { bedrockStateful("weedle", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("weedle", "blink")}
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("weedle", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = STATIONARY_POSES + UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("weedle", "ground_idle2")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("weedle", "ground_walk")
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("weedle", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, shoulderOffset),
rootPart.createTransformation().addPosition(ModelPartTransformation.Z_AXIS, shoulderZOffset)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("weedle", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(ModelPartTransformation.X_AXIS, -shoulderOffset),
rootPart.createTransformation().addPosition(ModelPartTransformation.Z_AXIS, shoulderZOffset)
)
)
}
override fun getFaintAnimation(state: PosableState) = bedrockStateful("weedle", "faint")
}
@@ -1,59 +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.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class JellicentModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("jellicent")
override val head = getPart("head")
override var portraitScale = 1.13F
override var portraitTranslation = Vec3(-0.56, 1.64, 0.0)
override var profileScale = 0.51F
override var profileTranslation = Vec3(0.0, 1.05, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
override fun registerPoses() {
val blink = quirk { bedrockStateful("jellicent", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("jellicent", "render")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("jellicent", "render")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("jellicent", "faint") else null
}
@@ -1,57 +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.render.models.blockbench.pokemon.gen7
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.PoseType.Companion.UI_POSES
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class CrabominableModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("crabominable")
override val head = getPart("head")
override var portraitScale = 1.0F
override var portraitTranslation = Vec3(-0.55, 1.65, 0.0)
override var profileScale = 0.3F
override var profileTranslation = Vec3(0.0, 1.2, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
override fun registerPoses() {
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + UI_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("crabominable", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
animations = arrayOf(
singleBoneLook(),
bedrock("crabominable", "ground_idle"),
bedrock("crabominable", "ground_walk")
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isPosedIn(standing, walk)) bedrockStateful("crabominable", "faint") else null
}
@@ -20,9 +20,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.*
import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animation.BedrockAnimationRepository
import com.cobblemon.mod.common.client.render.models.blockbench.blockentity.BlockEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.fossil.FossilModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.AncientPokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.BeastBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PokeBallPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen1.*
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen2.*
@@ -88,7 +86,7 @@ object VaryingModelRepository {
"bedrock/fossils/posers" to FossilModel::class.java,
"bedrock/block_entities/posers" to BlockEntityModel::class.java,
"bedrock/npcs/posers" to PosableModel::class.java,
"bedrock/poke_balls/posers" to PosableModel::class.java,
"bedrock/poke_balls/posers" to PokeBallPosableModel::class.java,
"bedrock/generic/posers" to PosableModel::class.java,
)
@@ -116,6 +114,7 @@ object VaryingModelRepository {
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(PosableModel::class.java, JsonModelAdapter(::PosableModel))
.registerTypeAdapter(PokemonPosableModel::class.java, JsonModelAdapter(::PokemonPosableModel))
.registerTypeAdapter(PokeBallPosableModel::class.java, JsonModelAdapter(::PokeBallPosableModel))
.registerTypeAdapter(FossilModel::class.java, JsonModelAdapter(::FossilModel))
.registerTypeAdapter(BlockEntityModel::class.java, JsonModelAdapter(::BlockEntityModel))
.registerTypeAdapter(Pose::class.java, PoseAdapter { JsonModelAdapter.model!! })
@@ -155,8 +154,8 @@ object VaryingModelRepository {
return {
var boneName = jsonObject.getAsJsonPrimitive("rootBone")
JsonModelAdapter.modelPart = if (boneName != null && it.children[boneName.asString] != null) it.children[boneName.asString] else it.children[fileName] ?: it.children.entries.filter { LocatorAccess.PREFIX !in it.key }.first().value
gson.fromJson(jsonObject, poserClass).also {
it.poses.forEach { (poseName, pose) -> pose.poseName = poseName }
gson.fromJson(jsonObject, poserClass).also { model ->
model.poses.forEach { (poseName, pose) -> pose.poseName = poseName }
}
}
}
@@ -169,61 +168,10 @@ object VaryingModelRepository {
}
fun registerInBuiltPosers() {
inbuilt("azure_ball", ::PokeBallModel)
inbuilt("beast_ball", ::BeastBallModel)
inbuilt("cherish_ball", ::PokeBallModel)
inbuilt("citrine_ball", ::PokeBallModel)
inbuilt("dive_ball", ::PokeBallModel)
inbuilt("dream_ball", ::PokeBallModel)
inbuilt("dusk_ball", ::PokeBallModel)
inbuilt("fast_ball", ::PokeBallModel)
inbuilt("friend_ball", ::PokeBallModel)
inbuilt("great_ball", ::PokeBallModel)
inbuilt("heal_ball", ::PokeBallModel)
inbuilt("heavy_ball", ::PokeBallModel)
inbuilt("level_ball", ::PokeBallModel)
inbuilt("love_ball", ::PokeBallModel)
inbuilt("lure_ball", ::PokeBallModel)
inbuilt("luxury_ball", ::PokeBallModel)
inbuilt("master_ball", ::PokeBallModel)
inbuilt("moon_ball", ::PokeBallModel)
inbuilt("nest_ball", ::PokeBallModel)
inbuilt("net_ball", ::PokeBallModel)
inbuilt("park_ball", ::PokeBallModel)
inbuilt("poke_ball", ::PokeBallModel)
inbuilt("premier_ball", ::PokeBallModel)
inbuilt("quick_ball", ::PokeBallModel)
inbuilt("repeat_ball", ::PokeBallModel)
inbuilt("roseate_ball", ::PokeBallModel)
inbuilt("safari_ball", ::PokeBallModel)
inbuilt("slate_ball", ::PokeBallModel)
inbuilt("sport_ball", ::PokeBallModel)
inbuilt("strange_ball", ::PokeBallModel)
inbuilt("timer_ball", ::PokeBallModel)
inbuilt("ultra_ball", ::PokeBallModel)
inbuilt("verdant_ball", ::PokeBallModel)
inbuilt("ancient_poke_ball", ::AncientPokeBallModel)
inbuilt("ancient_citrine_ball", ::AncientPokeBallModel)
inbuilt("ancient_verdant_ball", ::AncientPokeBallModel)
inbuilt("ancient_azure_ball", ::AncientPokeBallModel)
inbuilt("ancient_roseate_ball", ::AncientPokeBallModel)
inbuilt("ancient_slate_ball", ::AncientPokeBallModel)
inbuilt("ancient_ivory_ball", ::AncientPokeBallModel)
inbuilt("ancient_great_ball", ::AncientPokeBallModel)
inbuilt("ancient_ultra_ball", ::AncientPokeBallModel)
inbuilt("ancient_feather_ball", ::AncientPokeBallModel)
inbuilt("ancient_wing_ball", ::AncientPokeBallModel)
inbuilt("ancient_jet_ball", ::AncientPokeBallModel)
inbuilt("ancient_heavy_ball", ::AncientPokeBallModel)
inbuilt("ancient_leaden_ball", ::AncientPokeBallModel)
inbuilt("ancient_gigaton_ball", ::AncientPokeBallModel)
inbuilt("squirtle", ::SquirtleModel)
inbuilt("wartortle", ::WartortleModel)
inbuilt("caterpie", ::CaterpieModel)
inbuilt("metapod", ::MetapodModel)
inbuilt("butterfree", ::ButterfreeModel)
inbuilt("weedle", ::WeedleModel)
inbuilt("beedrill", ::BeedrillModel)
inbuilt("raticate", ::RaticateModel)
inbuilt("raticate_alolan", ::RaticateAlolanModel)
@@ -446,7 +394,6 @@ object VaryingModelRepository {
inbuilt("bayleef", ::BayleefModel)
inbuilt("meganium", ::MeganiumModel)
inbuilt("crabrawler", ::CrabrawlerModel)
inbuilt("crabominable", ::CrabominableModel)
inbuilt("wimpod", ::WimpodModel)
inbuilt("golisopod", ::GolisopodModel)
inbuilt("ralts", ::RaltsModel)
@@ -502,7 +449,6 @@ object VaryingModelRepository {
inbuilt("woobat", ::WoobatModel)
inbuilt("swoobat", ::SwoobatModel)
inbuilt("frillish", ::FrillishModel)
inbuilt("jellicent", ::JellicentModel)
inbuilt("cubchoo", ::CubchooModel)
inbuilt("beartic", ::BearticModel)
inbuilt("dreepy", ::DreepyModel)
@@ -676,4 +622,4 @@ object VaryingModelRepository {
}
}
}
}
}
@@ -12,8 +12,7 @@ import com.cobblemon.mod.common.api.reactive.Observable
import com.cobblemon.mod.common.api.reactive.SettableObservable
import com.cobblemon.mod.common.api.scheduling.Schedulable
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.AncientPokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PokeBallPosableModel
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import kotlin.random.Random
@@ -21,18 +20,21 @@ import kotlin.random.Random
abstract class PokeBallPosableState : PosableState(), Schedulable {
abstract val stateEmitter: SettableObservable<EmptyPokeBallEntity.CaptureState>
abstract val shakeEmitter: Observable<Unit>
protected open val group: String
get() = if (this.currentModel is AncientPokeBallModel) "ancient_poke_ball" else "poke_ball"
protected open var lastKnownState: EmptyPokeBallEntity.CaptureState? = null
open fun initSubscriptions() {
stateEmitter.subscribe { state ->
// if we fail to cast, provide default bone name.
val group = (currentModel as? PokeBallPosableModel)?.rootBone ?: "poke_ball"
when (state) {
EmptyPokeBallEntity.CaptureState.HIT -> {
doLater {
val model = this.currentModel!!
after(seconds = 0.2F) {
if (model is PokeBallModel && stateEmitter.get() == EmptyPokeBallEntity.CaptureState.HIT) {
if (model is PokeBallPosableModel && stateEmitter.get() == EmptyPokeBallEntity.CaptureState.HIT) {
doLater latest@{
model.moveToPose(this, model.poses["open"]!!)
after(seconds = 1.75F) {
@@ -60,7 +62,7 @@ abstract class PokeBallPosableState : PosableState(), Schedulable {
shakeEmitter
.pipe(Observable.emitWhile { stateEmitter.get() == EmptyPokeBallEntity.CaptureState.SHAKE })
.subscribe {
val bob = "bob${Random.Default.nextInt(6) + 1}"
val bob = "bob${Random.nextInt(6) + 1}"
doLater { setActiveAnimations(currentModel!!.bedrockStateful(group, bob)) }
}
}
@@ -79,4 +81,4 @@ abstract class PokeBallPosableState : PosableState(), Schedulable {
this.lastKnownState = state
}
}
}
}
@@ -9,7 +9,7 @@
package com.cobblemon.mod.common.client.render.pokeball
import com.cobblemon.mod.common.client.entity.EmptyPokeBallClientDelegate
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PosablePokeBallModel
import com.cobblemon.mod.common.client.render.models.blockbench.pokeball.PosablePokeBallEntityModel
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.render.models.blockbench.repository.VaryingModelRepository
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
@@ -24,7 +24,7 @@ import net.minecraft.client.renderer.texture.OverlayTexture
import net.minecraft.resources.ResourceLocation
class PokeBallRenderer(context: EntityRendererProvider.Context) : EntityRenderer<EmptyPokeBallEntity>(context) {
val model = PosablePokeBallModel()
val model = PosablePokeBallEntityModel()
override fun getTextureLocation(pEntity: EmptyPokeBallEntity): ResourceLocation {
return VaryingModelRepository.getTexture(pEntity.pokeBall.name, pEntity.delegate as EmptyPokeBallClientDelegate)
@@ -58,4 +58,4 @@ class PokeBallRenderer(context: EntityRendererProvider.Context) : EntityRenderer
poseStack.popPose()
super.render(entity, yaw, partialTicks, poseStack, buffer, packedLight)
}
}
}
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.OrientationControllable
import com.cobblemon.mod.common.api.pokedex.PokedexEntryProgress
import com.cobblemon.mod.common.client.CobblemonClient
import com.cobblemon.mod.common.client.battle.ClientBallDisplay
import com.cobblemon.mod.common.client.entity.EmptyPokeBallClientDelegate
import com.cobblemon.mod.common.client.entity.NPCClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate
import com.cobblemon.mod.common.client.entity.PokemonClientDelegate.Companion.BEAM_EXTEND_TIME
@@ -43,6 +44,7 @@ import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.math.DoubleRange
import com.cobblemon.mod.common.util.math.geometry.toRadians
import com.cobblemon.mod.common.util.math.remap
import com.cobblemon.mod.common.util.toRGBA
import com.mojang.blaze3d.vertex.PoseStack
import com.mojang.math.Axis
import net.minecraft.ChatFormatting
@@ -64,6 +66,7 @@ import net.minecraft.world.entity.Entity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.phys.Vec3
import org.joml.*
import java.lang.Integer
import kotlin.math.*
class PokemonRenderer(
@@ -320,6 +323,11 @@ class PokemonRenderer(
val pokemonPosition = entity.position().add(0.0, entity.bbHeight / 2.0 * clientDelegate.activeSendoutScale.toDouble(), 0.0)
val recallBallOrigin = (beamTarget as? Player)?.let { RecallArmAnimationTracker.get(it.uuid)?.beamOrigin }
val beamColorVec4f = when(beamTarget) {
is EmptyPokeBallEntity -> (beamTarget.delegate as EmptyPokeBallClientDelegate).getBeamColor()
else -> entity.pokemon.caughtBall.beamColor.rgb.toRGBA()
}
val beamSourcePosition = when {
// cover the recall ball origin presence
recallBallOrigin != null -> recallBallOrigin
@@ -385,10 +393,10 @@ class PokemonRenderer(
partialTicks = partialTicks,
totalLevelTime = entity.level().gameTime,
height = pokemonPosition.distanceTo(beamSourcePosition.add(offset)).toFloat() * ratio,
red = recallBeamColour.x,
green = recallBeamColour.y,
blue = recallBeamColour.z,
alpha = recallBeamColour.w,
red = beamColorVec4f.x,
green = beamColorVec4f.y,
blue = beamColorVec4f.z,
alpha = beamColorVec4f.w,
beamRadius = 0.03F,
glowRadius = 0.07F,
glowAlpha = 0.4F
@@ -30,7 +30,7 @@ object FishingRodTooltipGenerator : TooltipGenerator() {
val baitComponent = stack.get(CobblemonItemComponents.BAIT)
// Add the description of the Poke Ball used in the rod
ball.item.description.let {
ball.stack.item.description.let {
val bobberDescription = Component.translatable(
"cobblemon.pokerod.bobber",
it.copy().gray()
@@ -40,7 +40,7 @@ object FishingRodTooltipGenerator : TooltipGenerator() {
val client = Minecraft.getInstance()
val itemRegistry = client.level?.registryAccess()?.registryOrThrow(Registries.ITEM)
itemRegistry?.let { registry ->
itemRegistry?.let {
baitComponent?.stack?.item?.description
?.let { // maybe this can be simplified to not use the FishingBaits to get the stack and just use PokerodItem to get the stack since we have it already
val baitDescription = "cobblemon.pokerod.bait".asTranslated(it.copy().gray(), PokerodItem.getBaitStackOnRod(stack).count)
@@ -77,4 +77,4 @@ object FishingRodTooltipGenerator : TooltipGenerator() {
return resultLines
}
}
}
@@ -8,7 +8,6 @@
package com.cobblemon.mod.common.command
import com.cobblemon.mod.common.CobblemonSounds
import com.cobblemon.mod.common.api.permission.CobblemonPermissions
import com.cobblemon.mod.common.api.storage.pc.link.PCLinkManager
import com.cobblemon.mod.common.api.storage.pc.link.PermissiblePcLink
@@ -54,8 +53,8 @@ object PcCommand {
throw SimpleCommandExceptionType(lang("command.pc.invalid-box", box, pc.boxes.size).red()).create()
}
PCLinkManager.addLink(PermissiblePcLink(pc, player, CobblemonPermissions.PC))
OpenPCPacket(pc, box = if (box != null) (box - 1) else null).sendToPlayer(player)
OpenPCPacket(pc, box = if (box != null) (box - 1) else null, silent = true).sendToPlayer(player)
return Command.SINGLE_SUCCESS
}
}
}
@@ -8,6 +8,7 @@
package com.cobblemon.mod.common.entity.pokeball
import com.bedrockk.molang.runtime.MoLangRuntime
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
@@ -18,7 +19,9 @@ import com.cobblemon.mod.common.api.events.CobblemonEvents
import com.cobblemon.mod.common.api.events.pokeball.PokeBallCaptureCalculatedEvent
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
import com.cobblemon.mod.common.api.events.pokemon.PokemonCapturedEvent
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
import com.cobblemon.mod.common.api.net.serializers.StringSetDataSerializer
import com.cobblemon.mod.common.api.net.serializers.Vec3DataSerializer
import com.cobblemon.mod.common.api.pokeball.PokeBalls
@@ -49,7 +52,16 @@ import com.cobblemon.mod.common.net.messages.client.spawn.SpawnPokeballPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.pokemon.ai.PokemonBrain
import com.cobblemon.mod.common.pokemon.properties.UncatchableProperty
import com.cobblemon.mod.common.util.*
import com.cobblemon.mod.common.util.asArrayValue
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.lang
import com.cobblemon.mod.common.util.party
import com.cobblemon.mod.common.util.playSoundServer
import com.cobblemon.mod.common.util.sendParticlesServer
import com.cobblemon.mod.common.util.setPositionSafely
import com.cobblemon.mod.common.util.update
import com.cobblemon.mod.common.util.withQueryValue
import com.mojang.blaze3d.vertex.PoseStack
import net.minecraft.client.Minecraft
import net.minecraft.core.particles.ParticleTypes
@@ -65,7 +77,10 @@ import net.minecraft.server.level.ServerPlayer
import net.minecraft.sounds.SoundEvents
import net.minecraft.util.Mth
import net.minecraft.util.Mth.PI
import net.minecraft.world.entity.*
import net.minecraft.world.entity.EntityDimensions
import net.minecraft.world.entity.EntityType
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.Pose
import net.minecraft.world.entity.decoration.LeashFenceKnotEntity
import net.minecraft.world.entity.projectile.ThrowableItemProjectile
import net.minecraft.world.item.Item
@@ -97,7 +112,9 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
const val SECONDS_BETWEEN_SHAKES = 1.25F
const val SECONDS_BEFORE_SHAKE = 1F
val DIMENSIONS = EntityDimensions.fixed(0.4F, 0.4F)
val DIMENSIONS: EntityDimensions = EntityDimensions.fixed(0.4F, 0.4F)
val runtime = MoLangRuntime().setup()
}
val dataTrackerEmitter = SimpleObservable<EntityDataAccessor<*>>()
@@ -125,7 +142,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
.addFunction("thrower") { owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO }
override fun defineSynchedData(builder: SynchedEntityData.Builder) {
pokeBall = PokeBalls.POKE_BALL
pokeBall = PokeBalls.STRANGE_BALL
super.defineSynchedData(builder)
builder.define(CAPTURE_STATE, CaptureState.NOT.ordinal.toByte())
builder.define(ASPECTS, emptySet())
@@ -158,7 +175,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
addPosableFunctions(struct)
}
constructor(world: Level) : this(pokeBall = PokeBalls.POKE_BALL, world = world)
constructor(world: Level) : this(pokeBall = PokeBalls.STRANGE_BALL, world = world)
constructor(
pokeBall: PokeBall,
world: Level,
@@ -176,7 +193,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
this.pokeBall = pokeBall
}
var pokeBall: PokeBall = PokeBalls.POKE_BALL
var pokeBall: PokeBall = PokeBalls.STRANGE_BALL
override fun onHitBlock(hitResult: BlockHitResult) {
if (captureState == CaptureState.NOT) {
@@ -187,7 +204,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
discard()
val player = this.owner as? ServerPlayer
if (player?.isCreative == false) {
spawnAtLocation(defaultItem)
spawnAtLocation(pokeBall.stack())
}
}
} else {
@@ -245,7 +262,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
lang(
"capture.attempted_capture",
throwerActor.getName(),
pokeBall.item().description,
pokeBall.item().getDescriptionId(pokeBall.stack),
pokemonEntity.exposedSpecies.translatedName
).yellow()
)
@@ -283,7 +300,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
val owner = owner
discard()
val player = owner?.takeIf { it is ServerPlayer } as? ServerPlayer
if (player?.isCreative != true) spawnAtLocation(defaultItem)
if (player?.isCreative != true) spawnAtLocation(pokeBall.stack())
return
}
@@ -295,7 +312,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
delegate.tick(this)
if (level().isServerSide()) {
if (this.tickCount > 600 && this.capturingPokemon == null) {
this.remove(RemovalReason.DISCARDED)
@@ -345,7 +362,8 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
// Do a capture
val pokemon = capturingPokemon ?: return
val player = this.owner as? ServerPlayer ?: return
val captureTime = if (pokeBall.ancient == true) 1.8F else 1F
val captureTime = if (pokeBall.ancient) 1.8F else 1F
after(seconds = captureTime) {
// Dupes occurred by double-adding Pokémon, this hopefully prevents it triple-condom style
// wtf did I just read above me
@@ -354,8 +372,13 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
discard()
captureFuture.complete(true)
val party = player.party()
val effectsRuntime = runtime
.withQueryValue("pokeball", struct)
.withQueryValue("pokemon", pokemon.asMoLangValue())
pokemon.pokemon.caughtBall = pokeBall
pokeBall.effects.forEach { effect -> effect.apply(player, pokemon.pokemon) }
pokeBall.effects.forEach { effect -> effect.resolve(effectsRuntime) }
party.add(pokemon.pokemon)
val leashHolder = pokemon.leashHolder
@@ -379,7 +402,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
}
// Emits a shake by changing the value to the opposite of what it currently is. Sends an update to the client basically.
// We could replace this with a packet, but it feels awfully excessive when we already have 5 bajillion packets.
if (this.pokeBall.ancient) {
if (pokeBall.ancient) {
when (captureResult.numberOfShakes) {
1 -> ancientBallShake(setOf("q.bedrock_stateful('ancient_poke_ball', 'weirdhop')"))
2 -> ancientBallShake(setOf("q.bedrock_stateful('ancient_poke_ball', 'bighop')"))
@@ -394,7 +417,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
private fun breakFree() {
val pokemon = capturingPokemon ?: return
pokemon.setPositionSafely(position())
pokemon.beamMode = 2
pokemon.isInvisible = false
pokemon.isSilent = false
@@ -424,8 +447,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
after(seconds = 0.1F) {
captureFuture.complete(false)
val ballType =
this.pokeBall.name.path.lowercase().replace("_", "")
val ballType = pokeBall.name.path.lowercase().replace("_", "")
val mode = "casual"
val sendflash =
BedrockParticleOptionsRepository.getEffect(cobblemonResource("${ballType}/${mode}/sendflash"))
@@ -442,7 +464,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
override fun getDefaultItem(): Item = pokeBall.item()
override fun getDimensions(pPose: Pose) = DIMENSIONS
override fun getDimensions(pPose: Pose): EntityDimensions = DIMENSIONS
private fun attemptCatch(pokemonEntity: PokemonEntity) {
pokemonEntity.busyLocks.add(this)
@@ -550,4 +572,4 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
override fun waterDrag(): Float = this.pokeBall.waterDragValue
}
}
@@ -8,42 +8,31 @@
package com.cobblemon.mod.common.item
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.item.components.PokeBallComponent
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.isServerSide
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.server.level.ServerPlayer
import net.minecraft.world.InteractionHand
import net.minecraft.world.InteractionResultHolder
import net.minecraft.world.item.Rarity
import net.minecraft.world.entity.player.Player
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.Level
import kotlin.math.cos
class PokeBallItem(
val pokeBall: PokeBall
) : CobblemonItem(Properties().apply {
when (pokeBall.name) {
// Master balls are a netherite product and should be fire immune
PokeBalls.MASTER_BALL.name -> fireResistant().rarity(Rarity.EPIC)
PokeBalls.CHERISH_BALL.name -> rarity(Rarity.EPIC)
PokeBalls.ANCIENT_ORIGIN_BALL.name -> rarity(Rarity.EPIC)
PokeBalls.BEAST_BALL.name -> rarity(Rarity.RARE)
}
}) {
class PokeBallItem(val pokeBall: PokeBall) : CobblemonItem(pokeBall.itemProperties()) {
override fun use(world: Level, player: Player, usedHand: InteractionHand): InteractionResultHolder<ItemStack> {
val itemStack = player.getItemInHand(usedHand)
if (world.isServerSide()) {
throwPokeBall(world, player as ServerPlayer)
throwPokeBall(world, player as ServerPlayer, PokeBallComponent.getPokeBall(itemStack))
}
itemStack.consume(1, player)
return InteractionResultHolder.sidedSuccess(itemStack, world.isClientSide)
}
private fun throwPokeBall(world: Level, player: ServerPlayer) {
private fun throwPokeBall(world: Level, player: ServerPlayer, pokeBall: PokeBall) {
val pokeBallEntity = EmptyPokeBallEntity(pokeBall, player.level(), player).apply {
val overhandFactor: Float = if (player.xRot < 0) {
5f * cos(player.xRot.toRadians())
@@ -57,4 +46,7 @@ class PokeBallItem(
}
world.addFreshEntity(pokeBallEntity)
}
}
override fun getDescriptionId(itemStack: ItemStack): String =
PokeBallComponent.getPokeBall(itemStack).name.toLanguageKey("item")
}
@@ -0,0 +1,64 @@
/*
* 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.Cobblemon
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokeball.PokeBall
import com.mojang.serialization.Codec
import com.mojang.serialization.codecs.RecordCodecBuilder
import io.netty.buffer.ByteBuf
import net.minecraft.network.codec.ByteBufCodecs
import net.minecraft.network.codec.StreamCodec
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.ItemStack
data class PokeBallComponent(val name: ResourceLocation) {
companion object {
val CODEC: Codec<PokeBallComponent> = RecordCodecBuilder.create {
it
.group(ResourceLocation.CODEC.fieldOf("type").forGetter(PokeBallComponent::name))
.apply(it, ::PokeBallComponent)
}
val PACKET_CODEC: StreamCodec<ByteBuf, PokeBallComponent> = ByteBufCodecs.fromCodec(CODEC)
@JvmStatic
fun getPokeBall(stack: ItemStack): PokeBall {
return stack.get(CobblemonItemComponents.POKE_BALL)?.name?.let(PokeBalls::getPokeBall)
?: (stack.item as? PokeBallItem)?.pokeBall
?: PokeBalls.STRANGE_BALL
}
@JvmStatic
fun setPokeBall(stack: ItemStack, ball: PokeBall): ItemStack {
stack.set(CobblemonItemComponents.POKE_BALL, PokeBallComponent(ball.name))
return stack
}
@JvmStatic
fun createStack(ball: PokeBall?): ItemStack {
var result = PokeBalls.STRANGE_BALL
if (ball == null) {
Cobblemon.LOGGER.error("Attempted to create poke ball stack with null config, falling to strange ball.")
} else {
result = ball
}
val stack = ItemStack(CobblemonItems.STRANGE_BALL)
return setPokeBall(stack, result)
}
}
val ball: PokeBall
get() = PokeBalls.getPokeBall(name)
}
@@ -11,10 +11,12 @@ package com.cobblemon.mod.common.item.group
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItemComponents
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.riding.stats.RidingStat
import com.cobblemon.mod.common.api.tms.TechnicalMachines
import com.cobblemon.mod.common.block.TypeGemClusterBlock
import com.cobblemon.mod.common.item.AprijuiceItem
import com.cobblemon.mod.common.item.components.PokeBallComponent
import com.cobblemon.mod.common.item.components.RideBoostsComponent
import com.cobblemon.mod.common.item.components.TMMoveComponent
import com.cobblemon.mod.common.util.cobblemonResource
@@ -943,6 +945,11 @@ object CobblemonItemGroups {
private fun utilityItemEntries(displayContext: ItemDisplayParameters, entries: Output) {
CobblemonItems.pokeBalls.forEach(entries::accept)
PokeBalls.custom
.filter { (_, value) -> CobblemonItems.pokeBalls.none { it.pokeBall.name == value.name } }
.forEach { entries.accept(PokeBallComponent.createStack(it.value)) }
CobblemonItems.pokeRods.forEach(entries::accept)
CobblemonItems.pokedexes.forEach(entries::accept)
}
@@ -0,0 +1,107 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.data
import com.cobblemon.mod.common.api.pokedex.entry.CobblemonBiomeDexEvolutionRequirementDisplay
import com.cobblemon.mod.common.api.pokedex.entry.CobblemonDexEvolutionRequirementDisplay
import com.cobblemon.mod.common.api.pokedex.entry.DexEntries
import com.cobblemon.mod.common.api.pokedex.entry.DexEvolutionDisplay
import com.cobblemon.mod.common.api.pokedex.entry.DexEvolutionRequirementDisplay
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readNullable
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.readText
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeNullable
import com.cobblemon.mod.common.util.writeString
import com.cobblemon.mod.common.util.writeText
import com.google.common.collect.Lists
import com.google.common.collect.Sets
import net.minecraft.core.registries.BuiltInRegistries
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.util.ExtraCodecs
class DexEvolutionDisplaySyncPacket(entries: MutableList<DexEvolutionDisplay>)
: DataRegistrySyncPacket<DexEvolutionDisplay, DexEvolutionDisplaySyncPacket>(entries) {
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: DexEvolutionDisplay) {
buffer.writeIdentifier(entry.fromSpecies)
buffer.writeString(entry.fromForm)
buffer.writeCollection(entry.fromAspects) { _, aspect -> buffer.writeString(aspect) }
buffer.writeIdentifier(entry.toSpecies)
buffer.writeString(entry.toForm)
buffer.writeCollection(entry.toAspects) { _, aspect -> buffer.writeString(aspect) }
buffer.writeCollection(entry.requirements) { _, req ->
when (req) {
is CobblemonBiomeDexEvolutionRequirementDisplay -> {
buffer.writeByte(1)
buffer.writeIdentifier(BuiltInRegistries.ITEM.getKey(req.icon))
buffer.writeText(req.tooltip)
buffer.writeNullable(req.condition) { b, cond ->
b.writeIdentifier(cond.id)
b.writeBoolean(cond.tag)
}
buffer.writeNullable(req.antiCondition) { b, cond ->
b.writeIdentifier(cond.id)
b.writeBoolean(cond.tag)
}
}
else -> {
buffer.writeByte(0)
buffer.writeIdentifier(BuiltInRegistries.ITEM.getKey(req.icon))
buffer.writeText(req.tooltip)
}
}
}
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): DexEvolutionDisplay? {
val fromSpecies = buffer.readIdentifier()
val fromForm = buffer.readString()
val fromAspects = buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() }
val toSpecies = buffer.readIdentifier()
val toForm = buffer.readString()
val toAspects = buffer.readCollection(Sets::newHashSetWithExpectedSize) { buffer.readString() }
val requirements = buffer.readCollection(Lists::newArrayListWithExpectedSize) {
val type = buffer.readByte().toInt()
val icon = BuiltInRegistries.ITEM.get(buffer.readIdentifier())
val tooltip = buffer.readText().copy()
if (type == 1) {
val condition = buffer.readNullable { b ->
val id = b.readIdentifier()
val tag = b.readBoolean()
ExtraCodecs.TagOrElementLocation(id, tag)
}
val antiCondition = buffer.readNullable { b ->
val id = b.readIdentifier()
val tag = b.readBoolean()
ExtraCodecs.TagOrElementLocation(id, tag)
}
CobblemonBiomeDexEvolutionRequirementDisplay(icon, tooltip, condition, antiCondition)
} else {
CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
}.toList()
return DexEvolutionDisplay(fromSpecies, fromForm, fromAspects, toSpecies, toForm, toAspects, requirements)
}
override fun synchronizeDecoded(entries: Collection<DexEvolutionDisplay>) {
DexEntries.evolutionDisplays.clear()
DexEntries.evolutionDisplays.addAll(entries)
}
companion object {
val ID = cobblemonResource("dex_evolution_display_sync")
fun decode(buffer: RegistryFriendlyByteBuf): DexEvolutionDisplaySyncPacket =
DexEvolutionDisplaySyncPacket(mutableListOf()).apply { decodeBuffer(buffer) }
}
}
@@ -1,100 +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.data
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.pokemon.PokemonSpecies
import com.cobblemon.mod.common.api.pokemon.evolution.Evolution
import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
import com.cobblemon.mod.common.pokemon.FormData
import com.cobblemon.mod.common.pokemon.Species
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.readIdentifier
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeIdentifier
import com.cobblemon.mod.common.util.writeString
import com.google.gson.reflect.TypeToken
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
/**
* Carries the evolutions/preEvolution of every [Species] (and any [FormData] overrides), sent right after
* [SpeciesRegistrySyncPacket] so that every species is already registered by the time this is decoded. This avoids
* the cross-species lookup ordering hazards that come with resolving evolutions during the species sync itself.
*/
class SpeciesEvolutionSyncPacket(entries: Collection<SpeciesEvolutionEntry>) : DataRegistrySyncPacket<SpeciesEvolutionEntry, SpeciesEvolutionSyncPacket>(entries) {
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: SpeciesEvolutionEntry) {
try {
buffer.writeIdentifier(entry.speciesIdentifier)
buffer.writeString(PokemonSpecies.gson.toJson(entry.evolutions, EVOLUTION_SET_TYPE))
buffer.writeNullable(entry.preEvolution) { _, preEvolution -> buffer.writeString(PokemonSpecies.gson.toJson(preEvolution, PreEvolution::class.java)) }
buffer.writeCollection(entry.formOverrides) { _, override ->
buffer.writeString(override.formName)
buffer.writeNullable(override.evolutions) { _, evolutions -> buffer.writeString(PokemonSpecies.gson.toJson(evolutions, EVOLUTION_SET_TYPE)) }
buffer.writeNullable(override.preEvolution) { _, preEvolution -> buffer.writeString(PokemonSpecies.gson.toJson(preEvolution, PreEvolution::class.java)) }
}
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception encoding evolution data for species {}", entry.speciesIdentifier, e)
}
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): SpeciesEvolutionEntry? {
val identifier = buffer.readIdentifier()
return try {
val evolutions: MutableSet<Evolution> = PokemonSpecies.gson.fromJson(buffer.readString(), EVOLUTION_SET_TYPE)
val preEvolution = buffer.readNullable { _ -> PokemonSpecies.gson.fromJson(buffer.readString(), PreEvolution::class.java) }
val formOverrides = buffer.readList { _ ->
val formName = buffer.readString()
val formEvolutions = buffer.readNullable { _ -> PokemonSpecies.gson.fromJson<MutableSet<Evolution>>(buffer.readString(), EVOLUTION_SET_TYPE) }
val formPreEvolution = buffer.readNullable { _ -> PokemonSpecies.gson.fromJson(buffer.readString(), PreEvolution::class.java) }
FormEvolutionOverride(formName, formEvolutions, formPreEvolution)
}
SpeciesEvolutionEntry(identifier, evolutions, preEvolution, formOverrides)
} catch (e: Exception) {
Cobblemon.LOGGER.error("Caught exception decoding evolution data for species {}", identifier, e)
null
}
}
override fun synchronizeDecoded(entries: Collection<SpeciesEvolutionEntry>) {
entries.forEach { entry ->
val species = PokemonSpecies.getByIdentifier(entry.speciesIdentifier)
if (species == null) {
Cobblemon.LOGGER.error("Received evolution data for unknown species {}", entry.speciesIdentifier)
return@forEach
}
species.applyEvolutionData(entry.evolutions, entry.preEvolution)
entry.formOverrides.forEach { override ->
species.getFormByName(override.formName).applyEvolutionData(override.evolutions, override.preEvolution)
}
}
}
companion object {
val ID = cobblemonResource("species_evolution_sync")
private val EVOLUTION_SET_TYPE = TypeToken.getParameterized(MutableSet::class.java, Evolution::class.java).type
fun decode(buffer: RegistryFriendlyByteBuf): SpeciesEvolutionSyncPacket = SpeciesEvolutionSyncPacket(emptyList()).apply { decodeBuffer(buffer) }
}
}
class SpeciesEvolutionEntry(
val speciesIdentifier: ResourceLocation,
val evolutions: MutableSet<Evolution>,
val preEvolution: PreEvolution?,
val formOverrides: List<FormEvolutionOverride>
)
class FormEvolutionOverride(
val formName: String,
val evolutions: MutableSet<Evolution>?,
val preEvolution: PreEvolution?
)
@@ -30,8 +30,8 @@ class CaughtBallUpdatePacket(pokemon: () -> Pokemon?, value: PokeBall): SingleUp
val ID = cobblemonResource("caught_ball_update")
fun decode(buffer: RegistryFriendlyByteBuf): CaughtBallUpdatePacket {
val pokemon = decodePokemon(buffer)
val pokeBall = PokeBalls.getPokeBall(buffer.readIdentifier()) ?: PokeBalls.POKE_BALL
val pokeBall = PokeBalls.getPokeBall(buffer.readIdentifier())
return CaughtBallUpdatePacket(pokemon, pokeBall)
}
}
}
}
@@ -34,17 +34,19 @@ class OpenPCPacket : NetworkPacket<OpenPCPacket> {
val storeID: UUID
val box: Int?
val unseenWallpapers: Set<ResourceLocation>
val silent: Boolean
@JvmOverloads
@Deprecated("Use the constructor with the PCStore object instead, this will become private in a future title update", level = DeprecationLevel.WARNING)
constructor(storeID: UUID, box: Int? = null, unseenWallpapers: Set<ResourceLocation> = emptySet()) {
constructor(storeID: UUID, box: Int? = null, unseenWallpapers: Set<ResourceLocation> = emptySet(), silent: Boolean? = false) {
this.storeID = storeID
this.box = box
this.unseenWallpapers = unseenWallpapers
this.silent = silent ?: false
}
@JvmOverloads
constructor(pc: PCStore, box: Int? = null): this(pc.uuid, box, pc.unseenWallpapers)
constructor(pc: PCStore, box: Int? = null, silent: Boolean? = false): this(pc.uuid, box, pc.unseenWallpapers, silent ?: false)
override val id = ID
@@ -52,6 +54,7 @@ class OpenPCPacket : NetworkPacket<OpenPCPacket> {
buffer.writeUUID(storeID)
buffer.writeNullable(box) { buf, value -> buf.writeInt(value)}
buffer.writeCollection(unseenWallpapers) { _, it -> buffer.writeResourceLocation(it) }
buffer.writeBoolean(silent)
}
companion object {
@@ -59,7 +62,8 @@ class OpenPCPacket : NetworkPacket<OpenPCPacket> {
fun decode(buffer: RegistryFriendlyByteBuf) = OpenPCPacket(
buffer.readUUID(),
buffer.readNullable { it.readInt() },
buffer.readList { buffer.readResourceLocation() }.toSet()
buffer.readList { buffer.readResourceLocation() }.toSet(),
buffer.readBoolean()
)
}
}
}
@@ -8,18 +8,22 @@
package com.cobblemon.mod.common.pokeball
import com.bedrockk.molang.runtime.struct.QueryStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.StringValue
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokeBallFunctions
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.api.pokeball.catching.CaptureEffect
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.item.PokeBallItem
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.mojang.serialization.Codec
import net.minecraft.world.item.ItemStack
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.item.Item
import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Rarity
import java.awt.Color
/**
* Base poke ball object
@@ -31,37 +35,51 @@ import net.minecraft.resources.ResourceLocation
* @property waterDragValue The value of the water drag modifier when the entity travels, default is 0.8.
* @property model2d The identifier for the resource this Pokéball will use for the 2d model.
* @property model3d The identifier for the resource this Pokéball will use for the 3d model.
* @property ancient Marks this Pokéball as ancient, which makes it play different animations on catch hops
* @property beamColor Defines the beam color of pokeball
*/
open class PokeBall(
val name: ResourceLocation,
class PokeBall(
@Transient // Assigns in PokeBalls.reload
var name: ResourceLocation = cobblemonResource("strange_ball"),
val catchRateModifier: CatchRateModifier = CatchRateModifier.DUMMY,
val effects: List<CaptureEffect> = listOf(),
val effects: List<ExpressionLike> = listOf(),
val itemProperties: PokeBallProperties = PokeBallProperties(),
val waterDragValue: Float,
val model2d: ResourceLocation,
val model3d: ResourceLocation,
val throwPower: Float,
val ancient: Boolean
val ancient: Boolean = false,
val beamColor: Color
) {
val struct = QueryStruct(hashMapOf())
.addFunction("name") { StringValue(name.toString()) }
.addFunction("water_drag_value") { DoubleValue(waterDragValue) }
.addFunction("throw_power") { DoubleValue(throwPower) }
.addFunction("is_ancient") { DoubleValue(ancient) }
@Transient
val struct = ObjectValue(this)
.addPokeBallFunctions(this)
// .addFunction("item") { } // requires a registry which is hard
// This gets attached during item registry
internal lateinit var item: PokeBallItem
@Transient
internal lateinit var stack: ItemStack
fun item(): PokeBallItem = this.item
fun item(): PokeBallItem = this.stack.item as PokeBallItem
fun stack(count: Int = 1): ItemStack =
ItemStack(this.item(), count)
fun itemProperties(): Item.Properties {
var properties = Item.Properties().rarity(this.itemProperties.rarity)
if (this.itemProperties.fireResistant) {
properties = properties.fireResistant()
}
return properties
}
@Deprecated("This is a temporary solution for the safari ball dilemma", ReplaceWith("target.currentHealth"))
internal fun hpForCalculation(target: Pokemon): Int = if (this.name == PokeBalls.SAFARI_BALL.name) target.maxHealth else target.currentHealth
internal fun hpForCalculation(target: Pokemon): Int =
if (this.name == PokeBalls.SAFARI_BALL.name) target.maxHealth else target.currentHealth
companion object {
@JvmStatic
val BY_IDENTIFIER_CODEC: Codec<PokeBall> = CodecUtils.createByIdentifierCodec(
PokeBalls::getPokeBall,
@@ -70,4 +88,8 @@ open class PokeBall(
}
}
data class PokeBallProperties(
val fireResistant: Boolean = false,
val rarity: Rarity = Rarity.COMMON
)
}
@@ -46,7 +46,7 @@ object Gen1CaptureCalculator : CaptureCalculator {
}
// This value can be reused when calculating the shakes in case of breakout.
val nBound = when (pokeBall) {
PokeBalls.POKE_BALL -> 255
PokeBalls.POKE_BALL, PokeBalls.STRANGE_BALL -> 255
PokeBalls.GREAT_BALL -> 200
else -> 150
}
@@ -97,4 +97,4 @@ object Gen1CaptureCalculator : CaptureCalculator {
}
}
}
}
@@ -76,7 +76,7 @@ class FormData(
@SerializedName("moves")
private var _moves: Learnset? = null,
@SerializedName("evolutions")
private var _evolutions: MutableSet<Evolution>? = null,
private val _evolutions: MutableSet<Evolution>? = null,
@SerializedName("abilities")
private var _abilities: AbilityPool? = null,
@SerializedName("drops")
@@ -84,7 +84,7 @@ class FormData(
@SerializedName("pokedex")
private var _pokedex: MutableList<String>? = null,
@SerializedName("preEvolution")
private var _preEvolution: PreEvolution? = null,
private val _preEvolution: PreEvolution? = null,
private var standingEyeHeight: Float? = null,
private var swimmingEyeHeight: Float? = null,
private var flyingEyeHeight: Float? = null,
@@ -269,22 +269,13 @@ class FormData(
this.species = species
this.behaviour.parent = species.behaviour
Cobblemon.statProvider.provide(this)
this._lightingData?.let { this._lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
return this
}
/**
* Applies decoded evolution data onto this form, received separately via [com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket]
* after every species has already been registered.
*/
internal fun applyEvolutionData(evolutions: MutableSet<Evolution>?, preEvolution: PreEvolution?) {
this._evolutions = evolutions
this._preEvolution = preEvolution
// These properties are lazy, force resolution now that every species is guaranteed to be registered
// These properties are lazy, these need all species to be reloaded but SpeciesAdditions is what will eventually trigger this after all species have been loaded
this.preEvolution?.species
this.preEvolution?.form
this.evolutions.size
this._lightingData?.let { this._lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
return this
}
internal fun resolveEvolutionMoves() {
@@ -642,7 +642,16 @@ open class Pokemon : ShowdownIdentifiable {
scaleModifier = 1F + (roundedPercent / 100F)
}
var caughtBall: PokeBall = PokeBalls.POKE_BALL
var caughtBall: PokeBall = PokeBalls.STRANGE_BALL
get() {
return try {
field
} catch (_: Exception) {
// account for possibility of ball removal (datapack disable/removal)
CaughtBallUpdatePacket({ this }, PokeBalls.STRANGE_BALL)
PokeBalls.STRANGE_BALL
}
}
set(value) {
field = value
onChange(CaughtBallUpdatePacket({ this }, field))
@@ -181,20 +181,11 @@ class Species : ShowdownIdentifiable {
this.forms.add(0, this.standardForm)
}
this.lightingData?.let { this.lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
}
/**
* Applies decoded evolution data onto this species, received separately via [com.cobblemon.mod.common.net.messages.client.data.SpeciesEvolutionSyncPacket]
* after every species has already been registered.
*/
internal fun applyEvolutionData(evolutions: MutableSet<Evolution>, preEvolution: PreEvolution?) {
// Mutate in place rather than reassign: standardForm may have already captured this.evolutions by reference.
this.evolutions.clear()
this.evolutions.addAll(evolutions)
this.preEvolution = preEvolution
// These properties are lazy, these need all species to be reloaded but SpeciesAdditions is what will eventually trigger this after all species have been loaded
this.preEvolution?.species
this.preEvolution?.form
this.evolutions.size
behaviour.herd.initialize()
}
// Ran after initialize due to us creating a Pokémon here which requires all the properties in #initialize to be present for both this and the results, this is the easiest way to quickly resolve species + form
@@ -25,20 +25,12 @@ internal class CobblemonLazyPreEvolution(private val rawData: String) : PreEvolu
this.properties.species?.asIdentifierDefaultingNamespace()?.let { PokemonSpecies.getByIdentifier(it) } ?: throw IllegalArgumentException("A PreEvolution needs a valid species")
}
private fun computeForm(extraAspects: Set<String>): FormData {
val properties = this.properties
return properties.form?.let { formId -> this.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formId, true) } }
?: this.species.getForm(properties.aspects + this.rawData.split(" ").map { it.lowercase() }.toSet() + extraAspects)
private val lazyForm: FormData by lazy {
this.properties.form?.let { formId -> this.species.forms.firstOrNull { it.formOnlyShowdownId().equals(formId, true) } } ?: this.species.standardForm
}
private val lazyForm: FormData by lazy { computeForm(emptySet()) }
override val species: Species
get() = this.lazySpecies
override val form: FormData
get() = this.lazyForm
override fun resolveForm(extraAspects: Set<String>): FormData {
return if (extraAspects.isEmpty()) this.form else computeForm(extraAspects)
}
}
@@ -12,12 +12,10 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
import net.minecraft.world.phys.AABB
object BoxAdapter : JsonDeserializer<AABB>, JsonSerializer<AABB> {
object BoxAdapter : JsonDeserializer<AABB> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): AABB {
json as JsonObject
return AABB(
@@ -29,15 +27,4 @@ object BoxAdapter : JsonDeserializer<AABB>, JsonSerializer<AABB> {
json.get("maxZ")?.asDouble ?: 9999999.0
)
}
override fun serialize(src: AABB, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
val json = JsonObject()
json.addProperty("minX", src.minX)
json.addProperty("minY", src.minY)
json.addProperty("minZ", src.minZ)
json.addProperty("maxX", src.maxX)
json.addProperty("maxY", src.maxY)
json.addProperty("maxZ", src.maxZ)
return json
}
}
@@ -0,0 +1,87 @@
/*
* 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.util.adapters
import com.cobblemon.mod.common.api.pokeball.catching.CaptureEffect
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import com.google.common.collect.HashBiMap
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import net.minecraft.network.FriendlyByteBuf
import java.lang.reflect.Type
import kotlin.reflect.KClass
object CobblemonCaptureEffectAdapter : JsonDeserializer<CaptureEffect>, JsonSerializer<CaptureEffect> {
private const val VARIANT = "variant"
private val types = HashBiMap.create<String, KClass<out CaptureEffect>>()
val gson: Gson by lazy {
GsonBuilder()
.registerTypeAdapter(CaptureEffect::class.java, CobblemonCaptureEffectAdapter)
.setPrettyPrinting()
.create()
}
fun <T : CaptureEffect> registerType(id: String, type: KClass<T>) {
this.types[id.lowercase()] = type
}
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext,
): CaptureEffect {
val variant =
json.asJsonObject
.get(VARIANT)
.asString
.lowercase()
val type = getModifierType(variant)
return context.deserialize(json, type.java)
}
override fun serialize(
effect: CaptureEffect,
typeOfSrc: Type,
context: JsonSerializationContext,
): JsonElement {
val json = context.serialize(effect, effect::class.java).asJsonObject
val variant = getVariant(effect)
json.addProperty(VARIANT, variant)
return json
}
fun encode(effect: CaptureEffect, buffer: FriendlyByteBuf) {
buffer.writeString(gson.toJson(effect, CaptureEffect::class.java))
}
fun decode(buffer: FriendlyByteBuf): CaptureEffect = gson.fromJson(buffer.readString(), CaptureEffect::class.java)
private fun getVariant(effect: CaptureEffect): String {
val variant =
this.types.inverse()[effect::class]
?: throw IllegalArgumentException("Cannot resolve pokeball capture effect for type ${effect::class.qualifiedName}")
return variant
}
private fun getModifierType(variant: String): KClass<out CaptureEffect> {
val effectType =
this.types[variant] ?: throw IllegalArgumentException("Cannot resolve pokeball capture effect type for variant $variant")
return effectType
}
}
@@ -0,0 +1,113 @@
/*
* 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.util.adapters
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.pokeball.catching.CatchRateModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.BaseStatModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.CatchRateModifierAdapter
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.GuaranteedModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.LabelModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.ExpressionModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.PropertyBasedModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.ScriptModifier
import com.cobblemon.mod.common.api.pokeball.catching.modifiers.SimpleModifier
import com.cobblemon.mod.common.api.pokemon.PokemonProperties
import com.cobblemon.mod.common.util.readString
import com.cobblemon.mod.common.util.writeString
import com.google.common.collect.HashBiMap
import com.google.gson.Gson
import com.google.gson.GsonBuilder
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonElement
import com.google.gson.JsonSerializationContext
import net.minecraft.network.FriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import java.lang.reflect.Type
import kotlin.reflect.KClass
object CobblemonCatchRateModifierAdapter : CatchRateModifierAdapter {
private const val VARIANT = "variant"
private val types = HashBiMap.create<String, KClass<out CatchRateModifier>>()
init {
this.registerType(SimpleModifier.VARIANT, SimpleModifier::class)
this.registerType(BaseStatModifier.VARIANT, BaseStatModifier::class)
this.registerType(GuaranteedModifier.VARIANT, GuaranteedModifier::class)
this.registerType(PropertyBasedModifier.VARIANT, PropertyBasedModifier::class)
this.registerType(LabelModifier.VARIANT, LabelModifier::class)
this.registerType(ExpressionModifier.VARIANT, ExpressionModifier::class)
this.registerType(ScriptModifier.VARIANT, ScriptModifier::class)
}
val gson: Gson by lazy {
GsonBuilder()
.registerTypeAdapter(ResourceLocation::class.java, IdentifierAdapter)
.registerTypeAdapter(Expression::class.java, ExpressionAdapter)
.registerTypeAdapter(ExpressionLike::class.java, ExpressionLikeAdapter)
.registerTypeAdapter(PokemonProperties::class.java, pokemonPropertiesShortAdapter)
.registerTypeAdapter(CatchRateModifier::class.java, CobblemonCatchRateModifierAdapter)
.setPrettyPrinting()
.create()
}
override fun <T : CatchRateModifier> registerType(id: String, type: KClass<T>) {
this.types[id.lowercase()] = type
}
override fun deserialize(
json: JsonElement,
typeOfT: Type,
context: JsonDeserializationContext,
): CatchRateModifier {
val variant =
json.asJsonObject
.get(VARIANT)
.asString
.lowercase()
val type = getModifierType(variant)
return context.deserialize(json, type.java)
}
override fun serialize(
modifier: CatchRateModifier,
typeOfSrc: Type,
context: JsonSerializationContext,
): JsonElement {
val json = context.serialize(modifier, modifier::class.java).asJsonObject
val variant = getVariant(modifier)
json.addProperty(VARIANT, variant)
return json
}
fun encode(modifier: CatchRateModifier, buffer: FriendlyByteBuf) {
buffer.writeString(gson.toJson(modifier, CatchRateModifier::class.java))
}
fun decode(buffer: FriendlyByteBuf): CatchRateModifier =
gson.fromJson(buffer.readString(), CatchRateModifier::class.java)
private fun getVariant(modifier: CatchRateModifier): String {
val variant =
this.types.inverse()[modifier::class]
?: throw IllegalArgumentException("Cannot resolve pokeball catchrate modifier for type ${modifier::class.qualifiedName}")
return variant
}
private fun getModifierType(variant: String): KClass<out CatchRateModifier> {
val requirementType =
this.types[variant]
?: throw IllegalArgumentException("Cannot resolve pokeball catchrate modifier type for variant $variant")
return requirementType
}
}
@@ -13,8 +13,6 @@ import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -24,7 +22,7 @@ import java.lang.reflect.Type
* @author Hiroku
* @since July 24th, 2022
*/
object DropEntryAdapter : JsonDeserializer<DropEntry>, JsonSerializer<DropEntry> {
object DropEntryAdapter : JsonDeserializer<DropEntry> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): DropEntry {
json as JsonObject
val entryClass = json.get("type")?.asString
@@ -32,12 +30,4 @@ object DropEntryAdapter : JsonDeserializer<DropEntry>, JsonSerializer<DropEntry>
?: DropEntry.defaultType
return ctx.deserialize(json, entryClass)
}
override fun serialize(src: DropEntry, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
val json = ctx.serialize(src, src::class.java).asJsonObject
val name = DropEntry.entryTypes.entries.find { it.value == src::class.java }?.key
?: throw IllegalArgumentException("Cannot resolve drop entry type for ${src::class.qualifiedName}")
json.addProperty("type", name)
return json
}
}
@@ -10,21 +10,13 @@ package com.cobblemon.mod.common.util.adapters
import com.bedrockk.molang.Expression
import com.bedrockk.molang.MoLang
import com.cobblemon.mod.common.util.getString
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
object ExpressionAdapter : JsonDeserializer<Expression>, JsonSerializer<Expression> {
object ExpressionAdapter : JsonDeserializer<Expression> {
override fun deserialize(json: JsonElement, typeOfT: Type, ctx: JsonDeserializationContext): Expression {
return MoLang.createParser(json.asString).parseExpression()
}
override fun serialize(src: Expression, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.getString())
}
}
@@ -13,9 +13,6 @@ import com.cobblemon.mod.common.util.asExpressionLike
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonPrimitive
import com.google.gson.JsonSerializationContext
import com.google.gson.JsonSerializer
import java.lang.reflect.Type
/**
@@ -25,7 +22,7 @@ import java.lang.reflect.Type
* @author Hiroku
* @since October 22nd, 2023
*/
object ExpressionLikeAdapter : JsonDeserializer<ExpressionLike>, JsonSerializer<ExpressionLike> {
object ExpressionLikeAdapter : JsonDeserializer<ExpressionLike> {
override fun deserialize(json: JsonElement, type: Type, ctx: JsonDeserializationContext): ExpressionLike {
return if (json.isJsonPrimitive) {
json.asString.asExpressionLike()
@@ -35,8 +32,4 @@ object ExpressionLikeAdapter : JsonDeserializer<ExpressionLike>, JsonSerializer<
throw IllegalArgumentException("Invalid expression JSON: $json")
}
}
override fun serialize(src: ExpressionLike, typeOfSrc: Type, ctx: JsonSerializationContext): JsonElement {
return JsonPrimitive(src.getString())
}
}
@@ -11,23 +11,22 @@ package com.cobblemon.mod.common.util.adapters
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.Environment
import com.cobblemon.mod.common.util.server
import com.google.gson.JsonElement
import com.mojang.serialization.DynamicOps
import com.mojang.serialization.JsonOps
import net.minecraft.client.Minecraft
import net.minecraft.core.RegistryAccess
import net.minecraft.resources.RegistryOps
import net.minecraft.core.registries.BuiltInRegistries
/**
* Utility for getting RegistryAccess with the correct client/server registry access.
* Used when as a fallback when manually encoding/decoding registry-backed values (e.g., items, predicates).
*/
object RegistryAccessResolver {
private val builtInAccess: RegistryAccess by lazy { RegistryAccess.fromRegistryOfRegistries(BuiltInRegistries.REGISTRY) }
fun getAccess(): RegistryAccess {
try {
Cobblemon.implementation
} catch (e: UninitializedPropertyAccessException) {
return RegistryAccess.EMPTY
return builtInAccess
}
if (Cobblemon.implementation.environment() == Environment.CLIENT) {
val minecraft = Minecraft.getInstance()
@@ -35,13 +34,12 @@ object RegistryAccessResolver {
return minecraft.level!!.registryAccess()
}
if (Cobblemon.implementation.environment() == Environment.SERVER) {
val server = server()
if (server != null)
return server.registryAccess()
}
return RegistryAccess.EMPTY
return builtInAccess
}
}
@@ -0,0 +1,33 @@
{
"portraitScale": 1,
"portraitTranslation": [0, 0, 0],
"profileScale": 1,
"profileTranslation": [0, 0, 0],
"rootBone": "ancient_ball",
"poses": {
"flying": {
"poseTypes": ["NONE"],
"condition": "q.capture_state() == 'NOT'",
"animations": ["q.bedrock('ancient_ball', 'throw')"],
"transitions": {
"open": "q.bedrock_stateful('ancient_ball', 'open')"
}
},
"open": {
"poseTypes": ["NONE", "PORTRAIT"],
"animations": ["q.bedrock('ancient_ball', 'open_idle')"],
"transitions": {
"shut": "q.bedrock_stateful('ancient_ball', 'shut')"
}
},
"shut": {
"poseTypes": ["NONE", "PORTRAIT"],
"animations": [
"q.bedrock('ancient_ball', 'shut_idle')"
],
"transitions": {
"open": "q.bedrock_stateful('ancient_ball', 'open')"
}
}
}
}
@@ -0,0 +1,33 @@
{
"portraitScale": 1,
"portraitTranslation": [0, 0, 0],
"profileScale": 1,
"profileTranslation": [0, 0, 0],
"rootBone": "poke_ball",
"poses": {
"flying": {
"poseTypes": ["NONE"],
"condition": "q.capture_state() == 'NOT'",
"animations": ["q.bedrock('poke_ball', 'throw')"],
"transitions": {
"open": "q.bedrock_stateful('poke_ball', 'open')"
}
},
"open": {
"poseTypes": ["NONE", "PORTRAIT"],
"animations": ["q.bedrock('poke_ball', 'open_idle')"],
"transitions": {
"shut": "q.bedrock_stateful('poke_ball', 'shut')"
}
},
"shut": {
"poseTypes": ["NONE", "PORTRAIT"],
"animations": [
"q.bedrock('poke_ball', 'shut_idle')"
],
"transitions": {
"open": "q.bedrock_stateful('poke_ball', 'open')"
}
}
}
}
@@ -364,7 +364,7 @@
"bones": {
"body": {
"rotation": [55, 0, 0],
"position": [0, "-8+Math.cos((query.anim_time) * 90 * 2) * 0.2", -1]
"position": [0, "-3+Math.cos((query.anim_time) * 90 * 2) * 0.2", -1]
},
"torso2": {
"rotation": ["-12.5+Math.cos((query.anim_time-0.4) * 90 * 2) * 2", 0, 0]
@@ -426,16 +426,16 @@
"body": {
"rotation": [55, 0, 0],
"position": {
"0.0": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5],
"0.25": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 0.5],
"0.5": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.5],
"0.6667": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.17],
"0.75": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.25],
"0.7917": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.92],
"1.0": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 1],
"1.2083": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.25],
"1.375": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.75],
"1.5": [0, "-9+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5]
"0.0": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5],
"0.25": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 0.5],
"0.5": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.5],
"0.6667": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.17],
"0.75": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 5.25],
"0.7917": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 3.92],
"1.0": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", 1],
"1.2083": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.25],
"1.375": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.75],
"1.5": [0, "-3+Math.cos((query.anim_time) * 90 * (2+2/3)) * 0.2", -1.5]
}
},
"torso2": {
@@ -313,7 +313,7 @@
"bones": {
"body": {
"rotation": [47.5, 0, 0],
"position": [0, "-10+Math.cos((query.anim_time) * 90 * 1) * 0.3", 0]
"position": [0, "-6+Math.cos((query.anim_time) * 90 * 1) * 0.3", 0]
},
"torso": {
"rotation": [35, 0, 0],
@@ -426,7 +426,7 @@
"bones": {
"body": {
"rotation": [47.5, 0, 0],
"position": [0, -10, "Math.cos((query.anim_time-0.5) * 90 * (2+2/3)) * 4"]
"position": [0, -6, "Math.cos((query.anim_time-0.5) * 90 * (2+2/3)) * 4"]
},
"torso": {
"rotation": [35, 0, 0],
@@ -1925,7 +1925,7 @@
},
"body": {
"rotation": ["68.5-Math.cos((query.anim_time)*360/1-20)*-3", 0, 0],
"position": [0, "-24+Math.cos((query.anim_time)*360/1-90)*-0.5", "-1+Math.cos((query.anim_time)*360/1-45)*4"]
"position": [0, "Math.cos((query.anim_time)*360/1-90)*-0.5", "-1+Math.cos((query.anim_time)*360/1-45)*4"]
},
"neck": {
"rotation": [-7.5, 0, 0]
@@ -96,13 +96,13 @@
"parent": "head",
"pivot": [0, 6, 0.25],
"cubes": [
{"origin": [-2.4, 4.4, -5.28], "size": [1, 1, 0], "inflate": 0.01, "pivot": [-2, 5, 0], "rotation": [0, 0, -45], "uv": [12, 19]},
{"origin": [-1.75, 5.25, 5.76], "size": [1, 1, 0], "pivot": [-2, 5, 0], "rotation": [0, 0, -45], "uv": [8, 10]},
{"origin": [1.25, 1.25, 5.76], "size": [1, 1, 0], "pivot": [-2, 5, 0], "rotation": [0, 0, -45], "uv": [9, 2]},
{"origin": [-4.51, 6, -2.75], "size": [0, 1, 1], "pivot": [-2, 6.5, -2.25], "rotation": [-45, 0, 0], "uv": [7, 1]},
{"origin": [4.51, 6.75, -2.75], "size": [0, 1, 1], "pivot": [-2, 6.5, -2.25], "rotation": [-45, 0, 0], "uv": [0, 5]},
{"origin": [4.51, 8, 0.5], "size": [0, 1, 1], "pivot": [-2, 6.5, -2.25], "rotation": [-45, 0, 0], "uv": [0, 1]},
{"origin": [-4.51, 6.5, 1.25], "size": [0, 1, 1], "pivot": [-2, 7, 1.75], "rotation": [-45, 0, 0], "uv": [0, 6]}
{"origin": [-2.5, 4.4, -5.28], "size": [1, 1, 0], "uv": [12, 19]},
{"origin": [-2.5, 5.5, 5.76], "size": [1, 1, 0], "uv": [8, 10]},
{"origin": [2.5, 4.5, 5.76], "size": [1, 1, 0], "uv": [9, 2]},
{"origin": [-4.51, 6, -2.75], "size": [0, 1, 1], "uv": [7, 1]},
{"origin": [4.51, 6.5, -2.25], "size": [0, 1, 1], "uv": [0, 5]},
{"origin": [4.51, 5, 0.75], "size": [0, 1, 1], "uv": [0, 1]},
{"origin": [-4.51, 6.5, 1.75], "size": [0, 1, 1], "uv": [0, 6]}
]
},
{
@@ -119,7 +119,6 @@
"name": "eye_right",
"parent": "eyes",
"pivot": [-1.5, 5.75, -5.25],
"rotation": [0, 0, 5],
"cubes": [
{"origin": [-2.5, 6, -5.26], "size": [1, 2, 0], "uv": [8, 19]}
]
@@ -152,7 +151,6 @@
"name": "eye_left",
"parent": "eyes",
"pivot": [1.5, 5.75, -5.25],
"rotation": [0, 0, -5],
"cubes": [
{"origin": [1.5, 6, -5.26], "size": [1, 2, 0], "uv": [8, 8]}
]
@@ -112,7 +112,7 @@
{
"name": "head",
"parent": "neck",
"pivot": [0, 9, -0.25],
"pivot": [0, 8.25, -0.25],
"cubes": [
{"origin": [-4, 7.5, -3.75], "size": [8, 6, 7], "uv": [0, 0]},
{"origin": [-1.25, 9.25, -4.75], "size": [2, 2, 1], "inflate": 0.01, "uv": [23, 1]},
@@ -151,67 +151,75 @@
{
"name": "eye_left",
"parent": "eyes",
"pivot": [2.75, 11, -3.76],
"rotation": [0, 0, -7.5],
"pivot": [2.75, 10.75, -3.76]
},
{
"name": "iris_left",
"parent": "eye_left",
"pivot": [2.75, 10.875, -3.765],
"cubes": [
{"origin": [2, 10, -3.76], "size": [1.5, 2, 0], "uv": [0, 0]}
{"origin": [2, 9.75, -3.76], "size": [1.5, 2, 0], "uv": [0, 0]}
]
},
{
"name": "pupil_left",
"parent": "eye_left",
"pivot": [2.75, 11.25, -3.77],
"parent": "iris_left",
"pivot": [2.75, 11, -3.77],
"cubes": [
{"origin": [2.25, 10.5, -3.77], "size": [1, 1, 0], "uv": [2, 0]}
{"origin": [2.25, 10.25, -3.77], "size": [1, 1, 0], "uv": [2, 0]}
]
},
{
"name": "locator_eye_left",
"parent": "pupil_left",
"pivot": [2.75, 11, -4],
"pivot": [2.75, 10.75, -4],
"locators": {
"eye2": [2.75, 11, -4]
"eye2": [2.75, 10.75, -4]
}
},
{
"name": "eyelid_left",
"parent": "eye_left",
"pivot": [2.75, 11, -3.17],
"pivot": [2.75, 10.75, -3.17],
"cubes": [
{"origin": [1.75, 10, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 3]}
{"origin": [1.75, 9.75, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 3]}
]
},
{
"name": "eye_right",
"parent": "eyes",
"pivot": [-2.75, 11, -3.76],
"rotation": [0, 0, 7.5],
"pivot": [-2.75, 10.75, -3.76]
},
{
"name": "iris_right",
"parent": "eye_right",
"pivot": [-2.75, 10.875, -3.765],
"cubes": [
{"origin": [-3.5, 10, -3.76], "size": [1.5, 2, 0], "uv": [0, 2], "mirror": true}
{"origin": [-3.5, 9.75, -3.76], "size": [1.5, 2, 0], "uv": [0, 2], "mirror": true}
]
},
{
"name": "pupil_right",
"parent": "eye_right",
"pivot": [-2.75, 11.25, -3.77],
"parent": "iris_right",
"pivot": [-2.75, 11, -3.77],
"cubes": [
{"origin": [-3.25, 10.5, -3.77], "size": [1, 1, 0], "uv": [2, 1], "mirror": true}
{"origin": [-3.25, 10.25, -3.77], "size": [1, 1, 0], "uv": [2, 1], "mirror": true}
]
},
{
"name": "locator_eye_right",
"parent": "pupil_right",
"pivot": [-2.75, 11, -4],
"pivot": [-2.75, 10.75, -4],
"locators": {
"eye1": [-2.75, 11, -4]
"eye1": [-2.75, 10.75, -4]
}
},
{
"name": "eyelid_right",
"parent": "eye_right",
"pivot": [-2.75, 11, -3.17],
"pivot": [-2.75, 10.75, -3.17],
"cubes": [
{"origin": [-3.75, 10, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 15], "mirror": true}
{"origin": [-3.75, 9.75, -3.67], "size": [2, 2, 1], "inflate": 0.01, "uv": [1, 15], "mirror": true}
]
},
{
@@ -247,9 +247,7 @@
"pivot": [0, 8.5, 0],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [31, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [17, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [44, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [21, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [44, 0]}
]
},
{
@@ -267,9 +265,7 @@
"rotation": [0, 0, 67.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [67, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [49, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [80, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [37, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [80, 0]}
]
},
{
@@ -279,9 +275,7 @@
"rotation": [0, 0, -67.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [49, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [33, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [62, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [69, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [62, 0]}
]
},
{
@@ -299,9 +293,7 @@
"rotation": [0, 0, -137.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [85, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [81, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [98, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [85, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [98, 0]}
]
},
{
@@ -311,9 +303,7 @@
"rotation": [0, 0, 137.5],
"cubes": [
{"origin": [-2, 12, -2], "size": [4, 4, 4], "inflate": -0.25, "uv": [103, 2]},
{"origin": [2, 16, 2], "size": [-4, -4, -4], "uv": [65, 30]},
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [116, 0]},
{"origin": [1, 17.25, 1], "size": [-2, -1, -2], "inflate": -0.25, "uv": [53, 29]}
{"origin": [-1, 14.25, -1], "size": [2, 3, 2], "uv": [116, 0]}
]
},
{
@@ -0,0 +1,69 @@
{
"portraitScale": 2.1,
"portraitTranslation": [ -0.55, -1.55, 0 ],
"profileScale": 0.80,
"profileTranslation": [ 0, 0.55, 0 ],
"rootBone": "caterpie",
"animations": {
"cry": "q.bedrock_stateful('caterpie', 'cry')",
"faint": "q.bedrock_primary('caterpie', 'faint', q.curve('one'))"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.look('head')",
"q.bedrock('caterpie', 'ground_idle')"
],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"]
},
"walking": {
"poseTypes": ["WALK"],
"animations": [
"q.look('head')",
"q.bedrock('caterpie', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"]
},
"swimming": {
"poseTypes": ["WALK","SWIM"],
"isTouchingWater": true,
"animations": [
"q.pitch_tilt('body')",
"q.look('head')",
"q.bedrock('caterpie', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"]
},
"sleep": {
"poseTypes": ["SLEEP"],
"namedAnimations": {
"cry": "q.bedrock_stateful('dummy', 'cry')"
},
"animations": ["q.bedrock('caterpie', 'sleep')"]
},
"shoulder_left": {
"poseTypes": ["SHOULDER_LEFT"],
"animations": ["q.look('head')", "q.bedrock('caterpie', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [3, 0, 0]
}
]
},
"shoulder_right": {
"poseTypes": ["SHOULDER_RIGHT"],
"animations": ["q.look('head')", "q.bedrock('caterpie', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('caterpie', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [-3, 0, 0]
}
]
}
}
}
@@ -0,0 +1,69 @@
{
"portraitScale": 2.3,
"portraitTranslation": [-0.53, -1.9, 0],
"profileScale": 0.80,
"profileTranslation": [0, 0.51, 0],
"rootBone": "weedle",
"animations": {
"cry": "q.bedrock_stateful('weedle', 'cry')",
"faint": "q.bedrock_primary('weedle', 'faint', q.curve('one'))"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.look('head')",
"q.bedrock('weedle', 'ground_idle2')"
],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"]
},
"walking": {
"poseTypes": ["WALK"],
"animations": [
"q.look('head')",
"q.bedrock('weedle', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"]
},
"swimming": {
"poseTypes": ["WALK","SWIM"],
"isTouchingWater": true,
"animations": [
"q.pitch_tilt('body')",
"q.look('head')",
"q.bedrock('weedle', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"]
},
"sleep": {
"poseTypes": ["SLEEP"],
"namedAnimations": {
"cry": "q.bedrock_stateful('dummy', 'cry')"
},
"animations": ["q.bedrock('weedle', 'sleep')"]
},
"shoulder_left": {
"poseTypes": ["SHOULDER_LEFT"],
"animations": ["q.look('head')", "q.bedrock('weedle', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [3.8, 0, -3]
}
]
},
"shoulder_right": {
"poseTypes": ["SHOULDER_RIGHT"],
"animations": ["q.look('head')", "q.bedrock('weedle', 'ground_idle')"],
"quirks": ["q.bedrock_quirk('weedle', 'blink')"],
"transformedParts": [
{
"part": "body",
"position": [-3.8, 0, -3]
}
]
}
}
}
@@ -7,8 +7,9 @@
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"condition": "q.has_entity ? !q.in_air : 1.0",
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'ground_idle')"
],
"quirks": [
@@ -19,7 +20,7 @@
"poseTypes": ["WALK", "SWIM"],
"condition": "!q.is_sprinting",
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('mime_jr', 'blink')"]
@@ -28,7 +29,7 @@
"poseTypes": ["WALK", "SWIM"],
"condition": "q.is_sprinting",
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'ground_run')"
],
"quirks": ["q.bedrock_quirk('mime_jr', 'blink')"]
@@ -36,21 +37,33 @@
"shoulder_left": {
"poseTypes": ["SHOULDER_LEFT"],
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'shoulder_left')"
],
"quirks": [
"q.bedrock_quirk('mime_jr', 'blink')"
],
"transformedParts": [
{
"part": "body",
"position": [0, -0.25, 0]
}
]
},
"shoulder_right": {
"poseTypes": ["SHOULDER_RIGHT"],
"animations": [
"q.look('head_ai')",
"q.look('head')",
"q.bedrock('mime_jr', 'shoulder_right')"
],
"quirks": [
"q.bedrock_quirk('mime_jr', 'blink')"
],
"transformedParts": [
{
"part": "body",
"position": [0, -0.25, 0]
}
]
}
}
@@ -0,0 +1,42 @@
{
"portraitScale": 1.13,
"portraitTranslation": [-0.56, 1.64, 0],
"profileScale": 0.51,
"profileTranslation": [0, 1.05, 0],
"rootBone": "jellicent",
"animations": {
"recoil": "q.bedrock_stateful('jellicent', 'recoil')"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.look('head')",
"q.bedrock('jellicent', 'water_idle')"
],
"quirks": ["q.bedrock_quirk('jellicent', 'blink')"]
},
"walking": {
"poseTypes": ["WALK"],
"animations": [
"q.look('head')",
"q.bedrock('jellicent', 'water_swim')"
],
"quirks": ["q.bedrock_quirk('jellicent', 'blink')"]
},
"flying": {
"poseTypes": ["FLY", "SWIM"],
"animations": [
"q.pitch_tilt('body')",
"q.look('head')",
"q.bedrock('jellicent', 'water_swim')"
],
"quirks": ["q.bedrock_quirk('jellicent', 'blink')"]
},
"sleep": {
"poseTypes": ["SLEEP"],
"animations": ["q.bedrock('jellicent', 'sleep')"]
}
}
}
@@ -55,13 +55,7 @@
"q.look('head_ai')",
"q.bedrock('froakie', 'water_idle')"
],
"quirks": [],
"transformedParts": [
{
"part": "body",
"position": [0, -5, 0]
}
]
"quirks": []
},
"sleep": {
"poseTypes": ["SLEEP"],
@@ -10,8 +10,26 @@
"recoil": "q.bedrock_stateful('greninja', 'recoil')",
"physical": "q.bedrock_primary('greninja', 'physical', q.curve('symmetrical'))",
"lick": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"gunkshot": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"bounce": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"thief": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"pound": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"punch": "q.bedrock_primary('greninja', 'physical_tongue', q.curve('symmetrical'))",
"kick": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"lowkick": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"lowsweep": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"acrobatics": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"takedown": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"gigaimpact": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"liquidation": "q.bedrock_primary('greninja', 'physical_kick', q.curve('symmetrical'))",
"cut": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"aerialace": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"nightslash": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"falseswipe": "q.bedrock_primary('greninja', 'physical_katana', q.curve('symmetrical'))",
"watershuriken_2hit": "q.bedrock_primary('greninja', 'watershuriken_2hit', q.curve('symmetrical'))",
"watershuriken_3hit": "q.bedrock_primary('greninja', 'watershuriken_3hit', q.curve('symmetrical'))",
"watershuriken_4hit": "q.bedrock_primary('greninja', 'watershuriken_4hit', q.curve('symmetrical'))",
"watershuriken_5hit": "q.bedrock_primary('greninja', 'watershuriken_5hit', q.curve('symmetrical'))",
"special": "q.bedrock_primary('greninja', 'special', q.curve('symmetrical'))",
"status": "q.bedrock_primary('greninja', 'status', q.curve('symmetrical'))"
},
@@ -21,7 +39,7 @@
"isBattle": true,
"isTouchingWater": false,
"animations": [
"q.look('head_ai')",
"q.look('head_ai', -1, 1, 0, 0, 45, -45)",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'battle_idle')"
],
@@ -30,14 +48,22 @@
},
"quirks": ["q.bedrock_quirk('greninja', 'blink')","q.bedrock_quirk('greninja','battle_quirk', 20, 40, 1)"]
},
"profile": {
"poseTypes": ["PORTRAIT", "PROFILE"],
"animations": [
"q.look('head_ai')",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'ground_idle')"
]
},
"standing": {
"poseTypes": ["STAND", "NONE", "PORTRAIT", "PROFILE"],
"poseTypes": ["STAND", "NONE"],
"isBattle": false,
"isTouchingWater": false,
"animations": [
"q.look('head_ai')",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'ground_idle')"
"q.bedrock('greninja', math.random(0, 1) < 0.4 ? 'ground_idle_alt' : 'ground_idle')"
],
"quirks": [
"q.bedrock_quirk('greninja', q.array('quirk_look_left', 'quirk_look_right'), 8, 30, 1)"
@@ -75,7 +101,13 @@
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'water_swim')"
],
"quirks": []
"quirks": [],
"transformedParts": [
{
"part": "body",
"position": [0, 18, 0]
}
]
},
"surface-float": {
"poseTypes": ["STAND","FLOAT"],
@@ -97,6 +129,7 @@
"swimming": {
"poseTypes": ["WALK","SWIM"],
"isTouchingWater": true,
"isUnderWater": true,
"animations": [
"q.pitch_tilt('body')",
"q.look('head_ai')",
@@ -107,19 +140,26 @@
"transformedParts": [
{
"part": "body",
"position": [0, 24, 0]
"position": [0, 5, 0]
}
]
},
"float": {
"poseTypes": ["STAND","FLOAT"],
"isTouchingWater": true,
"isUnderWater": true,
"animations": [
"q.look('head_ai')",
"q.look('tongue1', -1, 1, 70, -45, 0, 0)",
"q.bedrock('greninja', 'water_idle')"
],
"quirks": []
"quirks": [],
"transformedParts": [
{
"part": "body",
"position": [0, 5, 0]
}
]
},
"sleep": {
"poseTypes": ["SLEEP"],
@@ -0,0 +1,28 @@
{
"portraitScale": 1.0,
"portraitTranslation": [-0.55, 1.65, 0],
"profileScale": 0.3,
"profileTranslation": [0, 1.2, 0],
"rootBone": "crabominable",
"animations": {
"cry": "q.bedrock_stateful('crabominable', 'cry')"
},
"poses": {
"standing": {
"poseTypes": ["STAND", "NONE", "FLOAT", "PORTRAIT", "PROFILE"],
"animations": [
"q.bedrock('crabominable', 'ground_idle')"
],
"quirks": ["q.bedrock_quirk('crabominable', 'blink')"]
},
"walking": {
"poseTypes": ["WALK", "SWIM"],
"animations": [
"q.bedrock('crabominable', 'ground_idle')",
"q.bedrock('crabominable', 'ground_walk')"
],
"quirks": ["q.bedrock_quirk('crabominable', 'blink')"]
}
}
}
@@ -31,6 +31,7 @@
"item.cobblemon.park_ball": "Park Ball",
"item.cobblemon.dream_ball": "Dream Ball",
"item.cobblemon.beast_ball": "Beast Ball",
"item.cobblemon.strange_ball": "Strange Ball",
"item.cobblemon.ancient_poke_ball": "Ancient Poké Ball",
"item.cobblemon.ancient_citrine_ball": "Ancient Citrine Ball",
"item.cobblemon.ancient_verdant_ball": "Ancient Verdant Ball",
@@ -1477,22 +1478,28 @@
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_species": "Evolves when traded with %1$s",
"cobblemon.ui.pokedex.evolution.trade.tooltip.with_any_species": "Evolves when traded",
"cobblemon.ui.pokedex.evolution.shedder.tooltip": "Also requires a spare Poké Ball and an empty party slot",
"cobblemon.ui.pokedex.evolution.or": "OR",
"cobblemon.ui.pokedex.evolution.one_of": "One of:",
"cobblemon.ui.pokedex.requirement.advancement.tooltip": "Player must have %1$s advancement",
"cobblemon.ui.pokedex.requirement.battle_critical_hits.tooltip": "Must hit %1$s critical-hits in battle",
"cobblemon.ui.pokedex.requirement.biome.anticondition.tooltip": "Must not be in the %1$s biome to evolve",
"cobblemon.ui.pokedex.requirement.biome.both.tooltip": "Must be in the %1$s biome and not in the %2$s biome to evolve",
"cobblemon.ui.pokedex.requirement.biome.tooltip": "Must be in the %1$s biome to evolve",
"cobblemon.ui.pokedex.requirement.biome.anticondition.tooltip": "Must not be in the %1$s biome",
"cobblemon.ui.pokedex.requirement.biome.both.tooltip": "Must be in the %1$s biome and not in the %2$s biome",
"cobblemon.ui.pokedex.requirement.biome.tooltip": "Must be in the %1$s biome",
"cobblemon.ui.pokedex.requirement.blocks_traveled.tooltip": "Must travel %1$s blocks",
"cobblemon.ui.pokedex.requirement.damage_taken.tooltip": "Must take %1$s damage in battle without fainting",
"cobblemon.ui.pokedex.requirement.defeat.tooltip": "Must defeat %1$s in battle to evolve",
"cobblemon.ui.pokedex.requirement.friendship.tooltip": "Requires at least %1$s friendship",
"cobblemon.ui.pokedex.requirement.has_move.tooltip": "Must know move %1$s",
"cobblemon.ui.pokedex.requirement.has_move_type.tooltip": "Must know a move of type %1$s",
"cobblemon.ui.pokedex.requirement.has_move_type.tooltip": "Must know a move of %1$s type",
"cobblemon.ui.pokedex.requirement.held_item.tooltip": "Must be holding %1$s",
"cobblemon.ui.pokedex.requirement.level.tooltip.min": "Evolves starting at level %1$s",
"cobblemon.ui.pokedex.requirement.level.tooltip.between": "Evolves between levels %1$s and %2$s",
"cobblemon.ui.pokedex.requirement.moon_phase.tooltip": "Must evolve in %1$s moon phase",
"cobblemon.ui.pokedex.requirement.nature.toxtricity_amped": "Must have a §aHardy§r, §aBrave§r, §aAdamant§r, §aNaughty§r, §aDocile§r, §aImpish§r, §aLax§r, §aHasty§r, §aJolly§r, §aNaive§r, §aRash§r, §aSassy§r, or §aQuirky§r nature",
"cobblemon.ui.pokedex.requirement.nature.toxtricity_low_key": "Must have a §aLonely§r, §aBold§r, §aRelaxed§r, §aTimid§r, §aSerious§r, §aModest§r, §aMild§r, §aQuiet§r, §aBashful§r, §aCalm§r, §aGentle§r, or §aCareful§r nature",
"cobblemon.ui.pokedex.requirement.negate.tooltip": "Not: %1$s",
"cobblemon.ui.pokedex.requirement.nickname.malamar": "Must be named §aDinnerbone§r or §aGrumm§r",
"cobblemon.ui.pokedex.requirement.party_member.anticondition.tooltip": "Party must not have a Pokémon with %1$s",
"cobblemon.ui.pokedex.requirement.party_member.tooltip": "Party must have a Pokémon with %1$s",
"cobblemon.ui.pokedex.requirement.pokemon_properties.tooltip": "Must be %1$s",
@@ -1507,9 +1514,9 @@
"cobblemon.ui.pokedex.requirement.recoil.tooltip": "Must suffer %1$s units of recoil in battle",
"cobblemon.ui.pokedex.requirement.stat_compare.tooltip": "Must have its %1$s higher than its %2$s",
"cobblemon.ui.pokedex.requirement.stat_equal.tooltip": "Must have its %1$s equals to its %2$s",
"cobblemon.ui.pokedex.requirement.structure.anticondition.tooltip": "Must not be in the %1$s structure to evolve",
"cobblemon.ui.pokedex.requirement.structure.both.tooltip": "Must be in the %1$s structure and not in the %2$s structure to evolve",
"cobblemon.ui.pokedex.requirement.structure.tooltip": "Must be in the %1$s structure to evolve",
"cobblemon.ui.pokedex.requirement.structure.anticondition.tooltip": "Must not be in the %1$s structure",
"cobblemon.ui.pokedex.requirement.structure.both.tooltip": "Must be in the %1$s structure and not in the %2$s structure",
"cobblemon.ui.pokedex.requirement.structure.tooltip": "Must be in the %1$s structure",
"cobblemon.ui.pokedex.requirement.time_range.tooltip": "Must evolve during the %1$s",
"cobblemon.ui.pokedex.requirement.use_move.tooltip": "Must use move %1$s in battle",
"cobblemon.ui.pokedex.requirement.weather.tooltip.raining": "Must be in rain to evolve",
@@ -3663,6 +3670,31 @@
"tag.item.cobblemon.tall_flowers": "Tall Flowers",
"tag.item.cobblemon.small_flowers": "Small Flowers",
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.bergmite_kalosbiome": "Kalosian",
"tag.worldgen.biome.cobblemon.evolution.regional.cubone_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.cubone_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.dartrix_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.dartrix_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.dewott_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.dewott_unovabiome": "Unovan",
"tag.worldgen.biome.cobblemon.evolution.regional.exeggcute_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.exeggcute_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.goomy_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.goomy_kalosbiome": "Kalosian",
"tag.worldgen.biome.cobblemon.evolution.regional.koffing_galarbiome": "Galarian",
"tag.worldgen.biome.cobblemon.evolution.regional.koffing_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.mimejr_galarbiome": "Galarian",
"tag.worldgen.biome.cobblemon.evolution.regional.mimejr_sinnohbiome": "Sinnohan",
"tag.worldgen.biome.cobblemon.evolution.regional.petilil_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.petilil_unovabiome": "Unovan",
"tag.worldgen.biome.cobblemon.evolution.regional.pikachu_alolabiome": "Alolan",
"tag.worldgen.biome.cobblemon.evolution.regional.pikachu_kantobiome": "Kantonian",
"tag.worldgen.biome.cobblemon.evolution.regional.quilava_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.quilava_johtobiome": "Johtonian",
"tag.worldgen.biome.cobblemon.evolution.regional.rufflet_hisuibiome": "Hisuian",
"tag.worldgen.biome.cobblemon.evolution.regional.rufflet_unovabiome": "Unovan",
"cobblemon.entity.variable.challenge_cooldown.name": "Challenge Cooldown",
"cobblemon.entity.variable.challenge_cooldown.desc": "How long the NPC will be un-challenge-able from that player's last challenge.",
"cobblemon.entity.variable.player_win_command.name": "Player Win Command",
@@ -6885,12 +6917,14 @@
"cobblemon.ui.pokedex.info.form.latios-mega": "Mega Latios",
"cobblemon.species.latios-mega.desc": "It flies at speeds exceeding Mach 4. It is among the top tier of all Pokémon in terms of aerial combat capability.",
"cobblemon.species.kyogre.name": "Kyogre",
"cobblemon.ui.pokedex.info.form.kyogre": "",
"cobblemon.species.kyogre.desc": "Kyogre is said to be the personification of the sea itself. Legends tell of its many clashes against Groudon, as each sought to gain the power of nature.",
"cobblemon.ui.pokedex.info.form.kyogre-primal": "Primal Kyogre",
"cobblemon.ui.pokedex.info.form.kyogre-primal": "Primal Reversion",
"cobblemon.species.kyogre-primal.desc": "Through Primal Reversion and with nature's full power, it will take back its true form. It can summon storms that cause the sea levels to rise.",
"cobblemon.species.groudon.name": "Groudon",
"cobblemon.ui.pokedex.info.form.groudon": "",
"cobblemon.species.groudon.desc": "Groudon is said to be the personification of the land itself. Legends tell of its many clashes against Kyogre, as each sought to gain the power of nature.",
"cobblemon.ui.pokedex.info.form.groudon-primal": "Primal Groudon",
"cobblemon.ui.pokedex.info.form.groudon-primal": "Primal Reversion",
"cobblemon.species.groudon-primal.desc": "Through Primal Reversion and with nature's full power, it will take back its true form. It can cause magma to erupt and expand the landmass of the world.",
"cobblemon.species.rayquaza.name": "Rayquaza",
"cobblemon.ui.pokedex.info.form.rayquaza": "",
@@ -7699,7 +7733,6 @@
"cobblemon.species.spewpa.name": "Spewpa",
"cobblemon.species.spewpa.desc": "The beaks of bird Pokémon can't begin to scratch its stalwart body. To defend itself, it spews powder.",
"cobblemon.species.vivillon.name": "Vivillon",
"cobblemon.ui.pokedex.info.form.vivillon": "",
"cobblemon.species.vivillon.desc": "The patterns on this Pokémon's wings depend on the climate and topography of its habitat. It scatters colorful scales.",
"cobblemon.ui.pokedex.info.form.vivillon-icy snow": "Icy Snow Pattern",
"cobblemon.species.vivillon-icy-snow.desc": "The patterns on this Pokémon depend on the climate and topography of the land it was born in. This form is from frigid lands.",
@@ -8156,11 +8189,8 @@
"cobblemon.ui.pokedex.info.form.silvally-fairy": "Type: Fairy",
"cobblemon.species.silvally-fairy.desc": "Thanks to its awakened powers, it can change to the Fairy type to match the memory it has equipped.",
"cobblemon.species.minior.name": "Minior",
"cobblemon.ui.pokedex.info.form.minior": "Core",
"cobblemon.species.minior.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-meteor": "Meteor Form",
"cobblemon.species.minior-meteor.desc": "This Pokémon is born in the ozone layer. It falls to the ground once the outer shell enclosing its core grows heavy.",
"cobblemon.ui.pokedex.info.form.minior-red": "Red Core",
"cobblemon.species.minior-red.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-orange": "Orange Core",
"cobblemon.species.minior-orange.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-yellow": "Yellow Core",
@@ -8173,6 +8203,8 @@
"cobblemon.species.minior-indigo.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-violet": "Violet Core",
"cobblemon.species.minior-violet.desc": "This is its form when its shell has fallen off. The color of its core depends on the materials that made up the food it ate.",
"cobblemon.ui.pokedex.info.form.minior-meteor": "Meteor Form",
"cobblemon.species.minior-meteor.desc": "This Pokémon is born in the ozone layer. It falls to the ground once the outer shell enclosing its core grows heavy.",
"cobblemon.species.komala.name": "Komala",
"cobblemon.species.komala.desc": "It remains asleep from birth to death as a result of the sedative properties of the leaves that form its diet.",
"cobblemon.species.turtonator.name": "Turtonator",
@@ -5094,9 +5094,9 @@
"cobblemon.species.trevenant.name": "Desséliande",
"cobblemon.species.trevenant.desc": "Ses racines sont un véritable système nerveux qui lui permet de contrôler les arbres de la forêt. Il se montre très gentil envers les Pokémon vivant dans son écorce.",
"cobblemon.species.pumpkaboo.name": "Pitrouille",
"cobblemon.species.pumpkaboo.desc": "Il paraît qu'il accompagne les âmes des défunts jusque dans l'au-delà, pour qu'elles puissent goûter au repos éternel.",
"cobblemon.species.pumpkaboo.desc": "Il a été confirmé récemment que chaque variété se distingue non seulement par sa taille, mais également par celle des âmes qu'elle enlève.",
"cobblemon.species.gourgeist.name": "Banshitrouye",
"cobblemon.species.gourgeist.desc": "Il enserre ses proies dans ses bras, semblables à de longues mèches de cheveux. Il se délecte de leur souffrance en chantonnant gaiement.",
"cobblemon.species.gourgeist.desc": "Les nuits de nouvelle lune, ce Pokémon erre dans les rues en chantant d'une voix lugubre. Ceux qui l'entendent sont maudits.",
"cobblemon.species.bergmite.name": "Grelaçon",
"cobblemon.species.bergmite.desc": "Il gèle ses ennemis d'une bourrasque à -100 °C. Il vit en troupeau avec ses congénères, dans les neiges éternelles des hautes montagnes.",
"cobblemon.species.avalugg.name": "Séracrawl",
@@ -5220,7 +5220,7 @@
"cobblemon.species.typenull.name": "Type:0",
"cobblemon.species.typenull.desc": "On lui fait porter un masque qui limite sa puissance afin d'éviter qu'il n'en perde le contrôle et sème le chaos.",
"cobblemon.species.silvally.name": "Silvallié",
"cobblemon.species.silvally.desc": "Il a réussi à briser son masque grâce à la force mentale qu'il a acquise en établissant un lien puissant avec son Dresseur.",
"cobblemon.species.silvally.desc": "Un lien fort avec son Dresseur lui a permis de libérer tout son potentiel.",
"cobblemon.species.minior.name": "Météno",
"cobblemon.species.minior.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.komala.name": "Dodoala",
@@ -5452,9 +5452,9 @@
"cobblemon.species.zacian.name": "Zacian",
"cobblemon.species.zacian.desc": "Également surnommé « héros légendaire », ce Pokémon absorbe du métal et se change en arme pour se battre.",
"cobblemon.species.zamazenta.name": "Zamazenta",
"cobblemon.species.zamazenta.desc": "Ce Pokémon sauva autrefois Galar en joignant ses forces à celles du roi des Hommes. Il absorbe du métal quand il passe au combat.",
"cobblemon.species.zamazenta.desc": "Ce Pokémon, changé en statue, est resté pétrifié si longtemps qu'il a failli tomber dans l'oubli.",
"cobblemon.species.eternatus.name": "Éthernatos",
"cobblemon.species.eternatus.desc": "Il devient actif en absorbant l'énergie qui émerge du sol de Galar grâce au noyau situé sur sa poitrine.",
"cobblemon.species.eternatus.desc": "Il se trouvait dans une météorite qui s'est écrasée il y a 20 000 ans. Il semble y avoir un lien entre ce Pokémon et le phénomène Dynamax.",
"cobblemon.species.kubfu.name": "Wushours",
"cobblemon.species.kubfu.desc": "Il suit un entraînement rigoureux pour parfaire ses techniques. La forme de son évolution dépend des capacités qu'il a assimilées.",
"cobblemon.species.urshifu.name": "Shifours",
@@ -5697,7 +5697,7 @@
"item.cobblemon.strawberry_sweet.tooltip": "Évolue Crèmy en Charmilly avec une Fraise en Sucre",
"item.cobblemon.clover_sweet.tooltip": "Évolue Crèmy en Charmilly avec un Trèfle en Sucre",
"item.cobblemon.galarica_cuff.tooltip": "Évolue Ramoloss de Galar en Flagadoss de Galar",
"item.cobblemon.link_cable.tooltip": "Évolue les Pokémon nécessitant un échange",
"item.cobblemon.link_cable.tooltip": "Permet d'évoluer un Pokémon nécessitant un échange",
"item.cobblemon.reaper_cloth.tooltip": "Évolue Téraclope en Noctunoir lorsque tenu lors d'un échange",
"item.cobblemon.star_sweet.tooltip": "Évolue Crèmy en Charmilly avec une Étoile en Sucre",
"cobblemon.config.ui.experience_share_multiplier.tooltip": "Multiplicateur de la part d'Expérience totale accordée à un Pokémon détenant un Multi Exp.",
@@ -6090,7 +6090,7 @@
"cobblemon.species.arceus-water.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.arceus-psychic.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.vivillon-garden.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres verdoyantes.",
"cobblemon.species.genesect-fire.desc": "En cours de recherche.",
"cobblemon.species.genesect-fire.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Feu. Il dispose de griffes d'acier tranchantes.",
"cobblemon.species.vivillon-tundra.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres glaciales.",
"cobblemon.species.vivillon-monsoon.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres où s'abattent des pluies diluviennes.",
"cobblemon.species.vivillon-modern.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres très ensoleillées.",
@@ -6140,8 +6140,8 @@
"cobblemon.species.slowpoke-galar.desc": "Bien qu'il ait toujours l'air un peu absent, il arrive que son visage se crispe brusquement. Son alimentation serait à l'origine du phénomène.",
"cobblemon.species.slowbro-mega.desc": "Il est complètement enveloppé dans le Kokiyas, qui forme un cocon impénétrable, mais somme toute plutôt confortable.",
"cobblemon.species.slowbro-galar.desc": "Le stimulus de la morsure du Kokiyas a déclenché une réaction chimique des épices retenues dans le corps de Flagadoss, lui donnant le type Poison.",
"cobblemon.species.farfetchd-galar.desc": "Les Canarticho ont pris cette forme à force de manier les poireaux de Galar, qui sont plus longs et plus larges que ceux des autres régions.",
"cobblemon.species.grimer-alola.desc": "Ce glouton, capable de dévorer tout et n'importe quoi, a été introduit à Alola pour résoudre les problèmes de déchets.",
"cobblemon.species.farfetchd-galar.desc": "À force de soulever son poireau lourd et massif, il a musclé son corps. Il compte sur la puissance destructrice d'une seule frappe pour gagner.",
"cobblemon.species.grimer-alola.desc": "Les cristaux sur son corps sont des agrégats de toxines. S'ils se détachent, des poisons mortels commencent à se répandre.",
"cobblemon.species.muk-alola.desc": "Ses crocs et ses griffes sont des toxines cristallisées. Il gangrène tout ce qu'il touche, alors n'approchez pas trop près.",
"cobblemon.species.gengar-mega.desc": "Grâce à la Méga-Évolution, il a accès à une autre dimension. Son corps déborde de puissance occulte.",
"cobblemon.species.gengar-gmax.desc": "Son corps est rempli d'énergie négative. Sa bouche gigantesque est un portail qui débouche sur l'autre monde.",
@@ -6190,7 +6190,7 @@
"cobblemon.species.unown-w.desc": "Ce Pokémon a la forme d'un caractère d'écriture antique. Personne ne sait si ces écritures antiques sont apparues avant les Zarbi ou le contraire. Des études sont en cours, mais aucun résultat n'a été annoncé.",
"cobblemon.species.unown-y.desc": "Ce Pokémon a la forme d'un caractère d'écriture antique. Personne ne sait si ces écritures antiques sont apparues avant les Zarbi ou le contraire. Des études sont en cours, mais aucun résultat n'a été annoncé.",
"cobblemon.species.unown-z.desc": "Ce Pokémon a la forme d'un caractère d'écriture antique. Personne ne sait si ces écritures antiques sont apparues avant les Zarbi ou le contraire. Des études sont en cours, mais aucun résultat n'a été annoncé.",
"cobblemon.species.steelix-mega.desc": "En cours de recherche.",
"cobblemon.species.steelix-mega.desc": "Il contrôle par magnétisme les fragments qui se sont détachés de la surface de son corps et s'en sert pour parer les attaques.",
"cobblemon.species.qwilfish-hisui.desc": "Il vit dans l'eau près des côtes froides. Ses épines empoisonnées sont plus épaisses et plus courtes que celle des Qwilfish des autres régions.",
"cobblemon.species.scizor-mega.desc": "Ses pinces lui servent davantage à frapper qu'à couper. Elles sont si lourdes qu'il lui est difficile de tenir quand le combat s'éternise.",
"cobblemon.species.heracross-mega.desc": "Le flot d'énergie décuple ses muscles, mais lui donne de terribles courbatures après avoir retrouvé sa forme d'origine.",
@@ -6198,20 +6198,20 @@
"cobblemon.species.corsola-galar.desc": "Il s'agit d'un Corayon mort il y a longtemps à cause d'un changement brutal de l'environnement. Il vole l'essence vitale des gens avec ses branches.",
"cobblemon.species.houndoom-mega.desc": "Son corps tout entier devient fiévreux lors de la Méga-Évolution. Il réduit ses adversaires en cendres avec son souffle enflammé.",
"cobblemon.species.tyranitar-mega.desc": "Il est encore plus violent en méga-évoluant, à tel point que l'on se demande s'il entend toujours les ordres de son Dresseur.",
"cobblemon.species.sceptile-mega.desc": "En cours de recherche.",
"cobblemon.species.blaziken-mega.desc": "En cours de recherche.",
"cobblemon.species.swampert-mega.desc": "En cours de recherche.",
"cobblemon.species.sceptile-mega.desc": "Pour projeter le bout de sa queue, il fait exploser les graines situées sur son dos, qui sont chargées de l'énergie de la Méga-Évolution.",
"cobblemon.species.blaziken-mega.desc": "Grâce au sang bouillant qui coule dans ses veines, ses techniques sont deux fois plus précises. Il soumet ses ennemis en un clin d'œil.",
"cobblemon.species.swampert-mega.desc": "En utilisant le même principe qu'une pompe hydraulique, l'eau dans son corps renforce encore plus ses muscles déjà considérablement gonflés.",
"cobblemon.species.zigzagoon-galar.desc": "Il court partout sans s'arrêter. Quand il rencontre un autre Pokémon, il fait exprès de lui rentrer dedans pour le provoquer.",
"cobblemon.species.linoone-galar.desc": "Il provoque ses adversaires avec sa longue langue pour les mettre en colère avant de les plaquer au sol avec un puissant tacle.",
"cobblemon.species.gardevoir-mega.desc": "En cours de recherche.",
"cobblemon.species.gardevoir-mega.desc": "Lorsqu'il déploie les plaques rouges sur sa poitrine et libère son esprit, il est capable de déchaîner ses plus grands pouvoirs psychiques.",
"cobblemon.species.sableye-mega.desc": "Son joyau est devenu immense et très lourd. Il lui permet de se protéger contre toute attaque, mais ralentit beaucoup ses mouvements.",
"cobblemon.species.mawile-mega.desc": "Ses deux gueules bougent violemment, comme mues par leur propre volonté. Elles peuvent pulvériser un rocher d'un seul claquement.",
"cobblemon.species.aggron-mega.desc": "En cours de recherche.",
"cobblemon.species.medicham-mega.desc": "En cours de recherche.",
"cobblemon.species.aggron-mega.desc": "L'énergie de la Méga-Évolution a transformé le fer dans l'organisme de ce Pokémon en une armure d'acier qui couvre tout son corps.",
"cobblemon.species.medicham-mega.desc": "Grâce à la Méga-Évolution, sa force spirituelle n'a plus de limites. Il matérialise des bras maléfiques par la pensée pour terrasser ses adversaires.",
"cobblemon.species.manectric-mega.desc": "L'électricité accumulée dans son corps lui permet de se déplacer à la vitesse de l'éclair, mais elle le rend aussi colérique.",
"cobblemon.species.sharpedo-mega.desc": "Quand il charge, des pics acérés surgissent sur sa tête ; il s'en sert pour blesser gravement ses adversaires.",
"cobblemon.species.camerupt-mega.desc": "En cours de recherche.",
"cobblemon.species.altaria-mega.desc": "En cours de recherche.",
"cobblemon.species.camerupt-mega.desc": "Quand sa colère atteint un point critique, l'immense volcan sur son dos entre en violente éruption et projette de la lave.",
"cobblemon.species.altaria-mega.desc": "Ses douces plumes si fragiles en apparence sont tellement robustes qu'une poignée suffit pour hisser un camion-benne.",
"cobblemon.species.castform-rainy.desc": "Morphéo adopte cette apparence uniquement les jours de pluie. On a tenté de reproduire ce phénomène sous une douche, sans succès.",
"cobblemon.species.castform-sunny.desc": "Morphéo adopte cette apparence uniquement les jours ensoleillés. On a tenté de reproduire ce phénomène avec un radiateur, sans succès.",
"cobblemon.species.castform-snowy.desc": "Morphéo adopte cette apparence uniquement les jours enneigés. On a tenté de reproduire ce phénomène avec un congélateur, sans succès.",
@@ -6220,14 +6220,14 @@
"cobblemon.species.absol-mega.desc": "L'énergie de la Méga-Évolution a fait se hérisser tous les poils de son corps. Comme il ne s'agit pas d'ailes, il ne peut pas voler.",
"cobblemon.species.salamence-mega.desc": "Il se met en position de vol en rangeant ses pattes avant dans sa carapace, et fonce en rase-mottes au-dessus des terrains accidentés.",
"cobblemon.species.metagross-mega.desc": "Quand il voit qu'il ne peut pas gagner, il attrape son adversaire avec ses griffes et lance le compte à rebours avant explosion.",
"cobblemon.species.latias-mega.desc": "En cours de recherche.",
"cobblemon.species.latios-mega.desc": "En cours de recherche.",
"cobblemon.species.latias-mega.desc": "Ses prouesses en vol et sa puissance de frappe se sont renforcées. Il disperse ses plumes transparentes pour perturber ses adversaires.",
"cobblemon.species.latios-mega.desc": "Il vole à une vitesse dépassant Mach 4. Il compte parmi les Pokémon les plus doués en combat aérien.",
"cobblemon.species.kyogre-primal.desc": "Grâce à l'énergie de la nature, il peut accomplir sa Primo-Résurgence pour retrouver son apparence originelle. Ce pouvoir lui permet d'appeler de terribles déluges pour étendre les mers.",
"cobblemon.species.groudon-primal.desc": "Grâce à l'énergie de la nature, il peut accomplir sa Primo-Résurgence pour retrouver son apparence originelle. Ce pouvoir lui permet de créer du magma pour étendre les continents.",
"cobblemon.species.rayquaza-mega.desc": "Un Pokémon qui vit dans la couche d'ozone et se nourrit des météorites qu'il intercepte. Leur énergie s'accumule dans son corps, ce qui lui permet de méga-évoluer.",
"cobblemon.species.deoxys-attack.desc": "En cours de recherche.",
"cobblemon.species.deoxys-defense.desc": "En cours de recherche.",
"cobblemon.species.deoxys-speed.desc": "En cours de recherche.",
"cobblemon.species.deoxys-attack.desc": "Deoxys a choisi lui-même d'avoir cette apparence agressive. Il trompe ses ennemis en changeant de forme à volonté.",
"cobblemon.species.deoxys-defense.desc": "Une aurore apparaît quand il change de forme. Il absorbe les attaques en altérant sa structure cellulaire.",
"cobblemon.species.deoxys-speed.desc": "Il est le résultat de la mutation d'un virus extraterrestre exposé à un rayon laser. Sa structure corporelle lui confère une agilité et une vitesse supérieures.",
"cobblemon.species.burmy-sandy.desc": "Il pétrit du sable et de la boue avec les fils de soie qu'il génère pour se confectionner une cape. Mais le vent et la pluie pouvant abimer celle-ci, Cheniti se cache dans les crevasses et les grottes.",
"cobblemon.species.burmy-trash.desc": "Lorsqu'il ne trouve pas de matière première adéquate pour confectionner sa cape, il utilise des détritus. Sa cape est d'ailleurs bien plus douillette qu'elle n'y parait.",
"cobblemon.species.wormadam-sandy.desc": "Son épiderme de sable et de boue très solide peut aisément repousser les coups de bec des Étourmi.",
@@ -6238,8 +6238,8 @@
"cobblemon.species.lopunny-mega.desc": "Ce Pokémon au tempérament violent se sert de ses oreilles comme s'il s'agissait de fouets pour corriger ses adversaires.",
"cobblemon.species.garchomp-mega.desc": "La Méga-Évolution l'a rendu plus brutal. Il taille en pièces ses ennemis à l'aide de ses bras en forme de faux.",
"cobblemon.species.lucario-mega.desc": "Son instinct guerrier s'est réveillé après son exposition à une explosion d'énergie. Il n'a absolument aucune pitié pour ses ennemis.",
"cobblemon.species.abomasnow-mega.desc": "En cours de recherche.",
"cobblemon.species.gallade-mega.desc": "En cours de recherche.",
"cobblemon.species.abomasnow-mega.desc": "Les bourgeons sur son dos ont donné naissance à d'imposantes épines de glace qui déclenchent des blizzards à −30 °C.",
"cobblemon.species.gallade-mega.desc": "Il tire sa force du lien qui l'unit à son Dresseur ou à sa Dresseuse. Son corps s'est doté de plaques tranchantes et d'une cape protectrice.",
"cobblemon.species.rotom-heat.desc": "Sous cette forme, Motisma adore utiliser la chaleur pour faire des bêtises, comme brûler les vêtements favoris des gens.",
"cobblemon.species.rotom-wash.desc": "Sous cette forme, Motisma adore utiliser de l'eau pour faire des bêtises, comme complètement inonder les pièces à vivre.",
"cobblemon.species.rotom-frost.desc": "Sous cette forme, Motisma adore utiliser l'air froid pour faire des bêtises, comme geler le bain chaud d'une personne avant qu'elle n'y mette le pied.",
@@ -6263,7 +6263,7 @@
"cobblemon.species.arceus-dragon.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.arceus-dark.desc": "Dans la mythologie, ce Pokémon existait déjà avant la formation de l'univers.",
"cobblemon.species.samurott-hisui.desc": "Cette fine lame au tempérament impitoyable est une évolution rare qui ne se produit qu'à Hisui. Ses attaques sont aussi ravageuses que les vagues déferlantes.",
"cobblemon.species.audino-mega.desc": "En cours de recherche.",
"cobblemon.species.audino-mega.desc": "Deux antennes ont poussé au cou de ce Pokémon. Il suffit de les toucher pour se retrouver plongé dans un profond sommeil.",
"cobblemon.species.lilligant-hisui.desc": "Ce Pokémon doit ses jambes musclées à son habitat dans les montagnes couvertes de neige épaisse. La fleur sur sa couronne exhale un parfum stimulant tout être alentour.",
"cobblemon.species.basculin-bluestriped.desc": "Ils forment des bancs entre spécimens de même couleur. Leurs plus grands ennemis sont les Bargantua rouges.",
"cobblemon.species.basculin-white.desc": "Ces Pokémon vivent en bancs dans les océans glacés et se partagent la nourriture. Quand le temps est venu, ils retournent à leur fleuve natal.",
@@ -6289,10 +6289,10 @@
"cobblemon.species.kyurem-white.desc": "Il prédit un futur où humains et Pokémon vivront dans un monde où la Réalité prévaut. Il fait tout son possible pour protéger cet avenir.",
"cobblemon.species.kyurem-black.desc": "On dit qu'il se bat pour défendre un monde Idéal dans lequel Pokémon et humains cohabiteront.",
"cobblemon.species.keldeo-resolute.desc": "Le pouvoir qui sommeillait au plus profond de lui s'est concentré dans sa corne, qui est devenue une lame capable de fendre n'importe quoi.",
"cobblemon.species.meloetta-pirouette.desc": "En cours de recherche.",
"cobblemon.species.genesect-water.desc": "En cours de recherche.",
"cobblemon.species.genesect-electric.desc": "En cours de recherche.",
"cobblemon.species.genesect-ice.desc": "En cours de recherche.",
"cobblemon.species.meloetta-pirouette.desc": "Meloetta a retrouvé un chant qui fut longtemps oublié. Ses pas de danse envoûtants peuvent prendre n'importe qui au piège.",
"cobblemon.species.genesect-water.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Eau. Il peut se transformer pour voler.",
"cobblemon.species.genesect-electric.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Électrik. Il peut tirer des boules d'énergie.",
"cobblemon.species.genesect-ice.desc": "Ce Pokémon prend cette forme quand on l'équipe d'un module chargé en énergie de type Glace. Il n'éprouve alors plus aucune compassion.",
"cobblemon.species.greninja-bond.desc": "En cours de recherche.",
"cobblemon.species.greninja-ash.desc": "En cours de recherche.",
"cobblemon.species.vivillon-polar.desc": "Ses motifs varient selon la topographie et le climat de l'endroit où il naît. Cette forme est originaire de terres enneigées.",
@@ -6331,10 +6331,10 @@
"cobblemon.species.furfrou-pharaoh.desc": "Il fut un temps où la noblesse organisait des concours pour déterminer quel Couafarel arborait la plus belle coupe.",
"cobblemon.species.meowstic-f.desc": "Les femelles sont un peu plus agressives et égoïstes que les mâles. Quand elles sont fâchées, elles se vengent avec leurs pouvoirs psychiques.",
"cobblemon.species.aegislash-blade.desc": "Sous cette forme, il excelle en attaque. Il utilise le poids et la puissance de l'acier pour détruire ses adversaires.",
"cobblemon.species.silvally-ghost.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-fire.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-electric.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-dragon.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-ghost.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Spectre grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-fire.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Feu grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-electric.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Électrik grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-dragon.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Dragon grâce à la ROM dont il est équipé.",
"cobblemon.species.minior-yellow.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.minior-meteor.desc": "Ce Pokémon vient de la couche d'ozone. Il s'écrase parfois au sol, lorsque la carapace qui enveloppe son noyau devient trop lourde.",
"cobblemon.species.minior-indigo.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
@@ -6370,21 +6370,21 @@
"cobblemon.species.klawf.desc": "Il se poste la tête en bas sur une falaise, dans l'attente d'une proie, mais il ne peut pas rester longtemps ainsi, car le sang lui monte à la tête.",
"cobblemon.species.wiglett.desc": "Il détecte l'odeur de Délestin à plus de vingt mètres, ce qui lui donne le temps de s'enfouir dans le sable.",
"cobblemon.species.flamigo.desc": "On pense qu'il fait un nœud à la base de son cou afin d'empêcher l'énergie qu'il accumule dans son ventre de s'échapper par son bec.",
"cobblemon.species.sliggoo-hisui.desc": "Ce Pokémon est de nature plutôt mélancolique. Le fer contenu dans les eaux de Hisui a agi sur son mucus épidermique et a changé sa peau en carapace de métal.",
"cobblemon.species.sliggoo-hisui.desc": "Sa coquille est devenue métallique en raison du fer contenu dans l'eau qu'il boit. Il n'en sort jamais lorsque le soleil brille.",
"cobblemon.species.goodra-hisui.desc": "Il peut moduler la rigidité de sa carapace de métal à volonté. Il abhorre la solitude et enrage si un être qu'il affectionne profondément l'abandonne.",
"cobblemon.species.pumpkaboo-small.desc": "Les Pitrouille Taille Mini aiment emporter l'âme des enfants vers l'au-delà.",
"cobblemon.species.pumpkaboo-large.desc": "Les Pitrouille Taille Maxi aiment emporter l'âme des adultes vers l'au-delà.",
"cobblemon.species.pumpkaboo-super.desc": "Les Pitrouille Taille Ultra adorent les esprits humains de très grande taille.",
"cobblemon.species.pumpkaboo-small.desc": "Cette variété emmène vers l'au-delà les âmes des enfants, dont la petite taille correspond à celle de son propre corps.",
"cobblemon.species.pumpkaboo-large.desc": "Cette variété emmène vers l'au-delà les âmes des adultes, dont la grande taille correspond à celle de son propre corps.",
"cobblemon.species.pumpkaboo-super.desc": "Cette variété emmène vers l'au-delà les âmes gigantesques, dont la taille correspond à celle de son propre corps.",
"cobblemon.species.gourgeist-small.desc": "Les Banshitrouye Taille Mini revêtent la forme d'enfants pour tromper les adultes et les emmener vers l'au-delà.",
"cobblemon.species.gourgeist-large.desc": "Les Banshitrouye Taille Maxi revêtent la forme d'adultes et prennent la main des enfants pour les emmener vers l'au-delà.",
"cobblemon.species.gourgeist-super.desc": "Les Banshitrouye Taille Ultra emmèneront de force n'importe qui vers l'au-delà, peu importe leur âge.",
"cobblemon.species.gourgeist-super.desc": "Les Banshitrouye gigantesques sont capables d'emmener n'importe qui de force vers l'au-delà.",
"cobblemon.species.avalugg-hisui.desc": "L'armure de glace qui enveloppe sa mâchoire inférieure peut briser des rocs plus durs que l'acier. Il se meut vélocement en fendant la neige épaisse des chemins de montagne.",
"cobblemon.species.xerneas-active.desc": "D'après la légende, quand ses bois brillent de sept couleurs, cela signifie qu'il prodigue la vie éternelle.",
"cobblemon.species.zygarde-10.desc": "La forme que Zygarde prend lorsqu'il rassemble 10% de ses Cellules éparpillées un peu partout. Il excelle au combat au corps-à-corps.",
"cobblemon.species.zygarde-10c.desc": "La forme que Zygarde prend lorsqu'il rassemble 10% de ses Cellules éparpillées un peu partout. Il excelle au combat au corps-à-corps.",
"cobblemon.species.zygarde-complete.desc": "La forme que Zygarde prend lorsqu'il rassemble 100% de ses Cellules. Sa puissance dépasse même celle de Xerneas ou d'Yveltal.",
"cobblemon.species.zygarde-50c.desc": "La forme que Zygarde prend lorsqu'il rassemble 50% de ses Cellules. Sa mission est de surveiller l'écosystème.",
"cobblemon.species.diancie-mega.desc": "En cours de recherche.",
"cobblemon.species.diancie-mega.desc": "On l'appelle aussi « la princesse rose de l'élégance ». Le diamant resplendissant qui orne son front pèse pas moins de 2 000 carats.",
"cobblemon.species.hoopa-unbound.desc": "Il possède une puissance considérable sous son apparence véritable. Une légende raconte qu'il désirait si fort un trésor caché dans un château qu'il déroba l'édifice tout entier.",
"cobblemon.species.decidueye-hisui.desc": "Les plumes d'Archéduc le protègent de la froideur glaciale de Hisui grâce à l'air logé dans leur tige. Voici une preuve que l'environnement agit sur l'évolution des Pokémon.",
"cobblemon.species.oricorio-pompom.desc": "Un Plumeline ayant bu du Nectar Jaune. Quand il aperçoit quelqu'un qui a une baisse de tension, il danse pour lui remonter le moral.",
@@ -6394,19 +6394,19 @@
"cobblemon.species.lycanroc-midnight.desc": "Cet habitué à la vie en solitaire n'obéit qu'aux Dresseurs qui savent exploiter tout le potentiel de sa force.",
"cobblemon.species.lycanroc-dusk.desc": "Ce Lougaroc a subi une évolution spéciale en étant irradié par les rayons du soleil couchant. Son calme cache une combativité hors norme.",
"cobblemon.species.wishiwashi-school.desc": "On l'appelle « le démon des mers ». Quand trop de Froussardine tombent sous les coups de l'ennemi, le banc reprend une activité normale.",
"cobblemon.species.silvally-fighting.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-flying.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-poison.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-ground.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-rock.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-bug.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-steel.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-water.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-grass.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-psychic.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-ice.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-dark.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-fairy.desc": "Il a enfin libéré tout son pouvoir et peut changer de type à volonté. L'énergie qui afflue en lui le fait briller d'une lueur argentée.",
"cobblemon.species.silvally-fighting.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Combat grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-flying.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Vol grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-poison.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Poison grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-ground.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Sol grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-rock.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Roche grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-bug.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Insecte grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-steel.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Acier grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-water.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Eau grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-grass.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Plante grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-psychic.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Psy grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-ice.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Glace grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-dark.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Ténèbres grâce à la ROM dont il est équipé.",
"cobblemon.species.silvally-fairy.desc": "Maintenant que son potentiel s'est éveillé, il peut adopter le type Fée grâce à la ROM dont il est équipé.",
"cobblemon.species.minior-orange.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.minior-green.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
"cobblemon.species.minior-blue.desc": "Météno se présente sous cette forme quand il perd sa carapace. La couleur de son noyau varie en fonction des composants de sa nourriture.",
@@ -6807,13 +6807,13 @@
"cobblemon.mark.ribbon_contest_super_master_cleverness": "Ruban Maître Intelligence",
"cobblemon.mark.ribbon_contest_super_master_cleverness.title": "%1$s le Génie",
"cobblemon.mark.ribbon_contest_super_master_coolness": "Ruban Maître Sang-froid",
"cobblemon.mark.ribbon_contest_super_master_coolness.title": "%1$s l'Ancienne Star",
"cobblemon.mark.ribbon_contest_super_master_coolness.title": "%1$s la Star",
"cobblemon.mark.ribbon_contest_super_master_coolness.desc": "Ruban commémorant une incroyable démonstration de Sang-froid durant un concours.",
"cobblemon.mark.ribbon_contest_super_master_cuteness": "Ruban Maître Grâce",
"cobblemon.mark.ribbon_contest_super_master_cuteness.title": "%1$s la Grâce Incarnée",
"cobblemon.mark.ribbon_contest_super_master_cuteness.desc": "Ruban commémorant une incroyable démonstration de Grâce durant un concours.",
"cobblemon.mark.ribbon_contest_super_master_toughness": "Ruban Maître Robustesse",
"cobblemon.mark.ribbon_contest_super_master_toughness.title": "%1$s l'Ancien Gros Dur",
"cobblemon.mark.ribbon_contest_super_master_toughness.title": "%1$s le Gros Dur",
"cobblemon.mark.ribbon_contest_super_master_toughness.desc": "Ruban commémorant une incroyable démonstration de Robustesse durant un concours.",
"cobblemon.mark.ribbon_contest_super_star": "Ruban Star des Concours",
"cobblemon.mark.ribbon_contest_super_star.title": "%1$s la Star de Légende",
@@ -6830,7 +6830,7 @@
"cobblemon.mark.ribbon_event_special.desc": "Ruban spécial pour un jour très particulier.",
"cobblemon.mark.ribbon_event_mystery_zone_sky": "Ruban Ciel",
"cobblemon.mark.ribbon_event_color_blue.desc": "Ruban commémoratif.",
"cobblemon.mark.mark_titan.desc": "L'insigne d'un ancien Pokémon Dominant.",
"cobblemon.mark.mark_titan.desc": "L'insigne d'un Pokémon Dominant.",
"cobblemon.mark.mark_personality_kindly.desc": "L'insigne d'un Pokémon bienveillant.",
"cobblemon.mark.mark_personality_slump.desc": "L'insigne d'un Pokémon blessé.",
"cobblemon.mark.ribbon_master_rank.desc": "Ruban commémorant la victoire contre un Dresseur de niveau Master Ball en combat classé.",
@@ -6947,8 +6947,8 @@
"cobblemon.mark.ribbon_training": "Ruban Perfectionnement",
"cobblemon.mark.ribbon_training.title": "%1$s le Surentraîné",
"cobblemon.mark.mark_alpha": "Insigne Baron",
"cobblemon.mark.mark_alpha.title": "%1$s l'Ancien Baron",
"cobblemon.mark.mark_alpha.desc": "L'insigne d'un ancien Pokémon Baron.",
"cobblemon.mark.mark_alpha.title": "%1$s le Baron",
"cobblemon.mark.mark_alpha.desc": "L'insigne d'un Pokémon Baron.",
"cobblemon.mark.mark_curry": "Insigne Curry",
"cobblemon.mark.mark_curry.title": "%1$s le Gastronome",
"cobblemon.mark.mark_curry.desc": "L'insigne d'un Pokémon gastronome.",
@@ -6982,7 +6982,7 @@
"cobblemon.mark.mark_revival.title": "%1$s le Ressuscité",
"cobblemon.mark.mark_revival.desc": "L'insigne d'un Pokémon ressuscité.",
"cobblemon.mark.mark_titan": "Insigne Dominant",
"cobblemon.mark.mark_titan.title": "%1$s l'Ancien Dominant",
"cobblemon.mark.mark_titan.title": "%1$s le Dominant",
"cobblemon.mark.mark_uncommon": "Insigne Sociabilité",
"cobblemon.mark.mark_uncommon.title": "%1$s le Sociable",
"cobblemon.mark.mark_uncommon.desc": "L'insigne d'un Pokémon sociable.",
@@ -7339,9 +7339,9 @@
"cobblemon.ui.pokedex.info.form.pinsir": " ",
"cobblemon.ui.pokedex.info.form.pinsir-mega": "Méga-Scarabrute",
"cobblemon.ui.pokedex.info.form.tauros": " ",
"cobblemon.ui.pokedex.info.form.tauros-paldeacombat": "Race Combative (Paldea)",
"cobblemon.ui.pokedex.info.form.tauros-paldeablaze": "Race Flamboyante (Paldea)",
"cobblemon.ui.pokedex.info.form.tauros-paldeaaqua": "Race Aquatique (Paldea)",
"cobblemon.ui.pokedex.info.form.tauros-paldeacombat": "Forme de Paldea (Race Combative)",
"cobblemon.ui.pokedex.info.form.tauros-paldeablaze": "Forme de Paldea (Race Flamboyante)",
"cobblemon.ui.pokedex.info.form.tauros-paldeaaqua": "Forme de Paldea (Race Aquatique)",
"cobblemon.ui.pokedex.info.form.gyarados": " ",
"cobblemon.ui.pokedex.info.form.gyarados-mega": "Méga-Léviator",
"cobblemon.ui.pokedex.info.form.lapras": " ",
@@ -7530,9 +7530,9 @@
"cobblemon.ui.pokedex.info.form.darumaka": " ",
"cobblemon.ui.pokedex.info.form.darumaka-galar": "Forme de Galar",
"cobblemon.ui.pokedex.info.form.darmanitan-zen": "Mode Transe",
"cobblemon.ui.pokedex.info.form.darmanitan-galar": "Forme de Galar",
"cobblemon.ui.pokedex.info.form.darmanitan-galar": "Forme de Galar du Mode Normal",
"cobblemon.ui.pokedex.info.form.darmanitan": " ",
"cobblemon.ui.pokedex.info.form.darmanitan-galarzen": "Mode Transe",
"cobblemon.ui.pokedex.info.form.darmanitan-galarzen": "Forme de Galar du Mode Transe",
"cobblemon.ui.pokedex.info.form.yamask": " ",
"cobblemon.ui.pokedex.info.form.yamask-galar": "Forme de Galar",
"cobblemon.ui.pokedex.info.form.garbodor": " ",
@@ -7566,10 +7566,10 @@
"cobblemon.ui.pokedex.info.form.keldeo-resolute": "Aspect Décidé",
"cobblemon.ui.pokedex.info.form.meloetta": "Forme Chant",
"cobblemon.ui.pokedex.info.form.meloetta-pirouette": "Forme Danse",
"cobblemon.ui.pokedex.info.form.genesect-water": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-electric": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-fire": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-ice": "Forme de Genesect",
"cobblemon.ui.pokedex.info.form.genesect-water": "Module Aqua",
"cobblemon.ui.pokedex.info.form.genesect-electric": "Module Choc",
"cobblemon.ui.pokedex.info.form.genesect-fire": "Module Pyro",
"cobblemon.ui.pokedex.info.form.genesect-ice": "Module Cryo",
"cobblemon.ui.pokedex.info.form.greninja": " ",
"cobblemon.ui.pokedex.info.form.greninja-bond": " ",
"cobblemon.ui.pokedex.info.form.greninja-ash": "Forme Sacha",
@@ -7613,14 +7613,14 @@
"cobblemon.ui.pokedex.info.form.sliggoo-hisui": "Forme de Hisui",
"cobblemon.ui.pokedex.info.form.goodra": " ",
"cobblemon.ui.pokedex.info.form.goodra-hisui": "Forme de Hisui",
"cobblemon.ui.pokedex.info.form.pumpkaboo": "Taille Normale",
"cobblemon.ui.pokedex.info.form.pumpkaboo-small": "Taille Mini",
"cobblemon.ui.pokedex.info.form.pumpkaboo-large": "Taille Maxi",
"cobblemon.ui.pokedex.info.form.pumpkaboo-super": "Taille Ultra",
"cobblemon.ui.pokedex.info.form.gourgeist": "Taille Normale",
"cobblemon.ui.pokedex.info.form.gourgeist-small": "Taille Mini",
"cobblemon.ui.pokedex.info.form.gourgeist-large": "Taille Maxi",
"cobblemon.ui.pokedex.info.form.gourgeist-super": "Taille Ultra",
"cobblemon.ui.pokedex.info.form.pumpkaboo": "Variété Moyenne",
"cobblemon.ui.pokedex.info.form.pumpkaboo-small": "Variété Petite",
"cobblemon.ui.pokedex.info.form.pumpkaboo-large": "Variété Grande",
"cobblemon.ui.pokedex.info.form.pumpkaboo-super": "Variété Giga",
"cobblemon.ui.pokedex.info.form.gourgeist": "Variété Moyenne",
"cobblemon.ui.pokedex.info.form.gourgeist-small": "Variété Petite",
"cobblemon.ui.pokedex.info.form.gourgeist-large": "Variété Grande",
"cobblemon.ui.pokedex.info.form.gourgeist-super": "Variété Giga",
"cobblemon.ui.pokedex.info.form.bergmite": " ",
"cobblemon.ui.pokedex.info.form.avalugg": " ",
"cobblemon.ui.pokedex.info.form.avalugg-hisui": "Forme de Hisui",
@@ -9198,5 +9198,10 @@
"stat.cobblemon.riding_liquid": "Distance parcourue par Pokémon dans un liquide",
"cobblemon.ui.ride_style.air.bird": "Oiseau",
"effect.cobblemon.cleanse_all": "Nettoyage des effets",
"effect.cobblemon.cleanse_negative": "Nettoyage des effets négatifs"
"effect.cobblemon.cleanse_negative": "Nettoyage des effets négatifs",
"item.cobblemon.sweet_heart": "Chococœur",
"item.cobblemon.sweet_heart.tooltip": "Une friandise au chocolat sucrée",
"item.cobblemon.technical_machine": "Capsule Technique",
"item.cobblemon.blank_tm": "CT Vierge",
"block.cobblemon.deepslate_crystal_core": "Cœur Cristal de profondoise"
}
@@ -3342,7 +3342,7 @@
"cobblemon.species.golbat.name": "ゴルバット",
"cobblemon.species.golbat.desc": "小さな 脚で 器用に 歩く。 寝ている 獲物に 忍びより キバを 突きたて 血を すするのだ。",
"cobblemon.species.oddish.name": "ナゾノクサ",
"cobblemon.species.oddish.desc": "昼間は 太陽を 避けるため 冷たい 地面に もぐっている。 月の光を 浴びて 育つ。",
"cobblemon.species.oddish.desc": "別名 §oアルキメンデス§r。 夜になると 2本の 根っこで 300メートルも 歩くという。",
"cobblemon.species.gloom.name": "クサイハナ",
"cobblemon.species.gloom.desc": "よだれのように 見える 甘い蜜。 とても ねばねば しており 触れると いつまでも まとわりつく。",
"cobblemon.species.vileplume.name": "ラフレシア",
@@ -3548,13 +3548,13 @@
"cobblemon.species.zapdos.name": "サンダー",
"cobblemon.species.zapdos.desc": "雷雲の 中に いると 言われる 伝説の ポケモン。 雷を 自在に 操る。",
"cobblemon.species.moltres.name": "ファイヤー",
"cobblemon.species.moltres.desc": "美しく 燃えあがる 翼で 山道を 照らし 遭難者を 助けたと 言い伝えられている。",
"cobblemon.species.moltres.desc": "伝説の とりポケモンの 1匹。 ファイヤーが 姿を 見せると 春が 訪れると 言われている。",
"cobblemon.species.dratini.name": "ミニリュウ",
"cobblemon.species.dratini.desc": "ながく 幻と 言われていた。 額にある 小さな 突起は 成長途中の ツノ。",
"cobblemon.species.dragonair.name": "ハクリュー",
"cobblemon.species.dragonair.desc": "きれいな 海や 湖に 棲む。 天候を 操る 力で 風に 乗り 空へと 昇る。",
"cobblemon.species.dragonite.name": "カイリュー",
"cobblemon.species.dragonite.desc": "海の 化身と 呼ばれる。 カイリュー型の 彫像を 舳先に 付ける 船も 多い。",
"cobblemon.species.dragonite.desc": "§o海の 化身§rと 呼ばれる。 カイリュー型の 彫像を 舳先に 付ける 船も 多い。",
"cobblemon.species.mewtwo.name": "ミュウツー",
"cobblemon.species.mewtwo.desc": "ミュウの 遺伝子を 組み替えて 生み出された。ポケモンで 一番 凶暴な 心を 持つという。",
"cobblemon.species.mew.name": "ミュウ",
@@ -3754,7 +3754,7 @@
"cobblemon.species.tyranitar.name": "バンギラス",
"cobblemon.species.tyranitar.desc": "地響きを 鳴らしながら 歩くと 大きな 山でさえ 崩れ まわりの 地形は 変わってしまう。",
"cobblemon.species.lugia.name": "ルギア",
"cobblemon.species.lugia.desc": "海の神様と 伝えられる ポケモン。 嵐の 夜 姿を 見たという 話が 伝えられる。",
"cobblemon.species.lugia.desc": "§o海の神様§rと 伝えられる ポケモン。 嵐の 夜 姿を 見たという 話が 伝えられる。",
"cobblemon.species.hooh.name": "ホウオウ",
"cobblemon.species.hooh.desc": "体は 七色に 輝き 飛んだあとは 虹が できると 神話に 残されている ポケモン。",
"cobblemon.species.celebi.name": "セレビィ",
@@ -3894,7 +3894,7 @@
"cobblemon.species.carvanha.name": "キバニア",
"cobblemon.species.carvanha.desc": "鋭い キバと たくましい 顎を もつ。 船乗りたちは キバニアの すみかには けっして 近づかない。",
"cobblemon.species.sharpedo.name": "サメハダー",
"cobblemon.species.sharpedo.desc": "海のギャングと 呼ばれ 生息 海域に 入りこんでしまった 船は もれなく 襲われてしまう。",
"cobblemon.species.sharpedo.desc": "§o海のギャング§rと 呼ばれ 生息 海域に 入りこんでしまった 船は もれなく 襲われてしまう。",
"cobblemon.species.wailmer.name": "ホエルコ",
"cobblemon.species.wailmer.desc": "海水を たくさん 飲みこむと 体が ボールの ように 弾む。 毎日 1トンの エサを 食べる。",
"cobblemon.species.wailord.name": "ホエルオー",
@@ -3916,7 +3916,7 @@
"cobblemon.species.vibrava.name": "ビブラーバ",
"cobblemon.species.vibrava.desc": "ハネを 成長 させるために 毎日 大量の 獲物を 消化液で 溶かして すする。",
"cobblemon.species.flygon.name": "フライゴン",
"cobblemon.species.flygon.desc": "はばたく 音が 女の人の 美しい 歌声に 似ているため 砂漠の精霊 と 呼ばれている。",
"cobblemon.species.flygon.desc": "はばたく 音が 女の人の 美しい 歌声に 似ているため §o砂漠の精霊§r と 呼ばれている。",
"cobblemon.species.cacnea.name": "サボネア",
"cobblemon.species.cacnea.desc": "雨の 少ない 厳しい 環境 ほど きれいで 香りの 強い 花を 咲かせる。 トゲの ついた 腕を 振り回して 戦う。",
"cobblemon.species.cacturne.name": "ノクタス",
@@ -3994,7 +3994,7 @@
"cobblemon.species.gorebyss.name": "サクラビス",
"cobblemon.species.gorebyss.desc": "獲物の 体液を 吸う。 肉は 海底に 沈み 他の ポケモンの エサに なるのだ。",
"cobblemon.species.relicanth.name": "ジーランス",
"cobblemon.species.relicanth.desc": "深海調査で 発見された。 1億年間 姿が 変わらず 生きている化石 と 呼ばれている。",
"cobblemon.species.relicanth.desc": "深海調査で 発見された。 1億年間 姿が 変わらず §o生きている化石§r と 呼ばれている。",
"cobblemon.species.luvdisc.name": "ラブカス",
"cobblemon.species.luvdisc.desc": "暖かい 海の サンゴ礁が 棲み処。 サニーゴの枝の 間で 眠るのが 特に お気に入り。",
"cobblemon.species.bagon.name": "タツベイ",
@@ -4020,9 +4020,9 @@
"cobblemon.species.latios.name": "ラティオス",
"cobblemon.species.latios.desc": "優しい 心の 持ち主にしか なつかない。 腕を 折り畳むと 空気抵抗が 減って ジェット機 よりも 速く 空を 飛べるぞ。",
"cobblemon.species.kyogre.name": "カイオーガ",
"cobblemon.species.kyogre.desc": "海の 化身 と 伝わる ポケモン。 自然のエネルギーを 求めて グラードンと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.kyogre.desc": "§o海の 化身§r と 伝わる ポケモン。 自然のエネルギーを 求めて グラードンと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.groudon.name": "グラードン",
"cobblemon.species.groudon.desc": "大地の 化身 と 伝わる ポケモン。自然のエネルギーを 求めて カイオーガと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.groudon.desc": "§o大地の 化身§r と 伝わる ポケモン。自然のエネルギーを 求めて カイオーガと 争いを 繰り返したという 伝説が ある。",
"cobblemon.species.rayquaza.name": "レックウザ",
"cobblemon.species.rayquaza.desc": "何億年も 生き続けていると いわれる。 グラードンと カイオーガの 争いを 治めたという 伝説が 残されている。",
"cobblemon.species.jirachi.name": "ジラーチ",
@@ -4160,7 +4160,7 @@
"cobblemon.species.skorupi.name": "スコルピ",
"cobblemon.species.skorupi.desc": "毒のある しっぽの ツメで 襲いかかる。 挟まれると 毒が 染みて 動けなくなる。",
"cobblemon.species.drapion.name": "ドラピオン",
"cobblemon.species.drapion.desc": "砂の悪魔と 呼ばれるほど 獰猛だが カバルドンには ケンカも 売らず おとなしい。",
"cobblemon.species.drapion.desc": "§o砂の悪魔§rと 呼ばれるほど 獰猛だが カバルドンには ケンカも 売らず おとなしい。",
"cobblemon.species.croagunk.name": "グレッグル",
"cobblemon.species.croagunk.desc": "毒を 薄めると 薬に なる。 薬品会社の マスコットに なって 人気者に なった。",
"cobblemon.species.toxicroak.name": "ドクロッグ",
@@ -4232,7 +4232,7 @@
"cobblemon.species.giratina.name": "ギラティナ",
"cobblemon.species.giratina.desc": "この世の 裏側にある 世界に 住んでいると 言われる ポケモン。 古代の 墓場に 現れる。",
"cobblemon.species.cresselia.name": "クレセリア",
"cobblemon.species.cresselia.desc": "クレセリアの 羽を 持って 寝ると 楽しい 夢が 見られると いう。 三日月の化身 と 呼ばれている。",
"cobblemon.species.cresselia.desc": "クレセリアの 羽を 持って 寝ると 楽しい 夢が 見られると いう。 §o三日月の化身§r と 呼ばれている。",
"cobblemon.species.phione.name": "フィオネ",
"cobblemon.species.phione.desc": "暖かい 海を 漂っている。 どんな 遠くに 流されても 生まれた 場所に かならず戻る。",
"cobblemon.species.manaphy.name": "マナフィ",
@@ -4316,7 +4316,7 @@
"cobblemon.species.drilbur.name": "モグリュー",
"cobblemon.species.drilbur.desc": "ツメで 地中を 掘り進むので 育てた 野菜が 傷むと 農業関係者は 嫌う。",
"cobblemon.species.excadrill.name": "ドリュウズ",
"cobblemon.species.excadrill.desc": "別名 ドリルキング。 地中を 掘り進む 速度は 時速150キロに 達する。",
"cobblemon.species.excadrill.desc": "別名 §oドリルキング§r。 地中を 掘り進む 速度は 時速150キロに 達する。",
"cobblemon.species.audino.name": "タブンネ",
"cobblemon.species.audino.desc": "心優しい ポケモン。 触角を 押し当て 相手の 気持ちや 体調を 理解する。",
"cobblemon.species.timburr.name": "ドッコラー",
@@ -4834,13 +4834,13 @@
"cobblemon.species.tapufini.name": "カプ・レヒレ",
"cobblemon.species.tapufini.desc": "水を 操る 遠い島の 守り神。 けがれを 払う 清らかな 水を 生みだすと 伝えられている。",
"cobblemon.species.cosmog.name": "コスモッグ",
"cobblemon.species.cosmog.desc": "大昔は 星の子 という 名で 呼ばれた。 別世界の ポケモンと いわれているが 詳しくは 不明。",
"cobblemon.species.cosmog.desc": "大昔は §o星の子§r という 名で 呼ばれた。 別世界の ポケモンと いわれているが 詳しくは 不明。",
"cobblemon.species.cosmoem.name": "コスモウム",
"cobblemon.species.cosmoem.desc": "死んだように まったく 動かないが 触れると ほのかに 温かい。 大昔は 星の繭と 呼ばれた。",
"cobblemon.species.cosmoem.desc": "死んだように まったく 動かないが 触れると ほのかに 温かい。 大昔は §o星の繭§rと 呼ばれた。",
"cobblemon.species.solgaleo.name": "ソルガレオ",
"cobblemon.species.solgaleo.desc": "太陽を 食らいし 獣 と かつて 呼ばれた。 無尽蔵の 光エネルギーを 放出する。",
"cobblemon.species.solgaleo.desc": "§o太陽を 食らいし 獣§r と かつて 呼ばれた。 無尽蔵の 光エネルギーを 放出する。",
"cobblemon.species.lunala.name": "ルナアーラ",
"cobblemon.species.lunala.desc": "月を 誘いし 獣 という 名前を もつ。 あらゆる 光を エネルギーに 変えて 生きている。",
"cobblemon.species.lunala.desc": "§o月を 誘いし 獣§r という 名前を もつ。 あらゆる 光を エネルギーに 変えて 生きている。",
"cobblemon.species.nihilego.name": "ウツロイド",
"cobblemon.species.nihilego.desc": "謎に 包まれた ウルトラビーストの 一種。 町ゆく 人が 寄生され 暴れだす 姿が 目撃された。",
"cobblemon.species.buzzwole.name": "マッシブーン",
@@ -4972,7 +4972,7 @@
"cobblemon.species.hattrem.name": "テブリム",
"cobblemon.species.hattrem.desc": "頭の房で 相手を 殴り 黙らせる。 プロボクサーさえ 一発 KOの 破壊力。",
"cobblemon.species.hatterene.name": "ブリムオン",
"cobblemon.species.hatterene.desc": "別名は 森の 魔女。 騒ぎたてると 触手のツメで 引き裂かれるかも しれないぞ。",
"cobblemon.species.hatterene.desc": "別名は §o森の 魔女§r。 騒ぎたてると 触手のツメで 引き裂かれるかも しれないぞ。",
"cobblemon.species.impidimp.name": "ベロバー",
"cobblemon.species.impidimp.desc": "民家に 忍びこみ 盗みを 働き さらに 悔しがる 人の マイナスエネルギーも いただくのだ。",
"cobblemon.species.morgrem.name": "ギモー",
@@ -5032,7 +5032,7 @@
"cobblemon.species.dragapult.name": "ドラパルト",
"cobblemon.species.dragapult.desc": "ツノに 入った ドラメシヤは マッハの スピードで 飛ばされるのを 心待ちに しているらしい。",
"cobblemon.species.zacian.name": "ザシアン",
"cobblemon.species.zacian.desc": "伝説の 英雄と 呼ばれる ポケモン。 金属を 取り込み 武具に 変化させ 戦う。",
"cobblemon.species.zacian.desc": "§o伝説の 英雄§rと 呼ばれる ポケモン。 金属を 取り込み 武具に 変化させ 戦う。",
"cobblemon.species.zamazenta.name": "ザマゼンタ",
"cobblemon.species.zamazenta.desc": "石像の ような 姿で 忘れ去られるほどの あいだ 眠りに ついていた ポケモン。",
"cobblemon.species.eternatus.name": "ムゲンダイナ",
@@ -5224,29 +5224,29 @@
"cobblemon.species.kingambit.name": "ドドゲザン",
"cobblemon.species.kingambit.desc": "ドドゲザンに 進化できるのは 大軍勢の 頂点に 立った 1匹の キリキザンだけなのだ。",
"cobblemon.species.greattusk.name": "イダイナキバ",
"cobblemon.species.greattusk.desc": "近年 目撃例が ある。 イダイナキバという 名は ある本に 記された 生物から 取られた。",
"cobblemon.species.greattusk.desc": "近年 目撃例が ある。 イダイナキバという 名は ある本に 記された 生物から 取られた。 ”§oパルデア未確認ファイル§r §oNo.09 太古から 生き続ける 恐竜!? 謎の生物 イダイナキバ§r §oパルデア某所にある 乾燥地帯において 謎の生物の 目撃が§r §o報告されている。 一見 ドンファンに 似た その生物は§r §o巨大な牙と 強固な鱗を 備えており 好戦的な性質を§r §o示したという。 現代に 生き残る 恐竜というのが 有力な説だが§r §oその正体は 不明。 イダイナキバ という名は かの奇書§r §oスカーレットブックに 記された 謎の生物から 命名されたと いわれる。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.screamtail.name": "サケブシッポ",
"cobblemon.species.screamtail.desc": "10億年前の プリンと オカルト雑誌が 紹介した 謎の ポケモンに 似ている。",
"cobblemon.species.screamtail.desc": "10億年前の プリンと オカルト雑誌が 紹介した 謎の ポケモンに 似ている。 ”§oパルデア未確認ファイル No.10§r §o10億年前の プリン 発見!? 謎の生物 サケブシッポ§r §oパルデア某所の 森で 目撃された 謎の生物が サケブシッポ§r §oである。 プリンに 酷似した 愛くるしさを 持っているが§r §o攻撃的な性質で 近づくものに 襲いかかるという。§r §o原始的な姿と 凶暴性から 一説には 10億年前の プリンと§r §o噂されている。 奇書 スカーレットブックに 記された§r §o叫ぶ尻尾と 呼ばれる 謎の生物が 名前の由来 である。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.brutebonnet.name": "アラブルタケ",
"cobblemon.species.brutebonnet.desc": "怪しい 雑誌が 恐竜と キノコの 合成と 紹介した ポケモンに 少し 似ているのだ。",
"cobblemon.species.brutebonnet.desc": "怪しい 雑誌が 恐竜と キノコの 合成と 紹介した ポケモンに 少し 似ているのだ。 ”§oパルデア未確認ファイル No.06§r §o恐竜?モロバレル? 謎の生物 アラブルタケ§r §o恐竜の特徴とモロバレルの特徴を あわせ持つと 噂される§r §o奇妙な生物。 奇書と 名高い スカーレットブックで 紹介された§r §o荒ぶる茸と 呼ばれる 謎の生物と おおむね 特徴が 一致。§r §oそこから この未確認生物は アラブルタケと 呼ばれるように§r §oなった。 通常の モロバレルより はるかに 身体は 巨大だが§r §o知性は低いようで 凶暴な ハードパンチャーらしい。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.fluttermane.name": "ハバタクカミ",
"cobblemon.species.fluttermane.desc": "オカルト雑誌で 取り上げられた 翼竜の 幽霊と 特徴だけは 似ているのだ。",
"cobblemon.species.fluttermane.desc": "オカルト雑誌で 取り上げられた 翼竜の 幽霊と 特徴だけは 似ているのだ。 ”§oパルデア未確認ファイル No.05§r §o翼竜のゴースト!? 謎の生物 ハバタクカミ§r §oその名はスカーレットブックと 呼ばれる 奇書の中に 登場する§r §o謎の生物から 名づけられた。 長い体毛を くねらせ 夜空を§r §oゆらゆらと 飛びまわると いわれる。 目撃者からの§r §o証言によると 一見 巨大なムウマの ようだが 気性が§r §oとても荒く 好戦的とのこと。 古代に 生息した 翼竜の魂から§r §o生まれた ゴーストという 説もある。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.slitherwing.name": "チヲハウハネ",
"cobblemon.species.slitherwing.desc": "古い 本で チヲハウハネと 紹介されている 生物に 似た点が ある 謎のポケモン。",
"cobblemon.species.slitherwing.desc": "古い 本で チヲハウハネと 紹介されている 生物に 似た点が ある 謎のポケモン。 ”§oパルデア未確認ファイル No.08§r §o蘇った太古の ウルガモス!? 謎の生物 チヲハウハネ§r §o目撃情報が少なく 極めて 謎の多い 未確認生物が チヲハウハネ§r §oである。 その名は 奇書として 知られる スカーレットブックに§r §o記された 謎の生物に ちなんで 名づけられた。 かつて 本誌が§r §oとりあげた 原始時代の ウルガモスと 共通する特徴が あり§r §o化石から 復元したとも 考えられる。 だが 過去に§r §oウルガモスの化石が 発見された記録は 存在していない。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.sandyshocks.name": "スナノケガワ",
"cobblemon.species.sandyshocks.desc": "オカルト雑誌の 記事にあった 1万年 生きた レアコイルの 姿に やや 似ている。",
"cobblemon.species.sandyshocks.desc": "オカルト雑誌の 記事にあった 1万年 生きた レアコイルの 姿に やや 似ている。 ”§oパルデア未確認ファイル No.07§r §o寿命1万年の レアコイル!? 謎の生物 スナノケガワ§r §o1万年生きた レアコイルの姿だと 主張する 科学者もいる§r §o謎の生物。 パルデアの 乾燥地帯を 中心に 数件の 目撃報告が§r §oあるものの いまだ 捕獲されたという 記録はない。§r §o攻撃的な性質で 目撃者が 襲われるケースも あるという。§r §o世紀の奇書 スカーレットブックで 紹介された 砂の毛皮と§r §o呼ばれる 謎の生物と 特徴が 一致している。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.irontreads.name": "テツノワダチ",
"cobblemon.species.irontreads.desc": "オカルト雑誌が 紹介した 宇宙人が 送り込んできた 科学兵器に よく 似ている。",
"cobblemon.species.irontreads.desc": "オカルト雑誌が 紹介した 宇宙人が 送り込んできた 科学兵器に よく 似ている。 ”§oパルデア未確認ファイル No.09§r §o宇宙人の科学兵器か!? 謎の物体 テツノワダチ§r §oパルデア某所にある 乾燥地帯において 謎の生物の 目撃が§r §o報告されている。 一見 ドンファンに 似た その生物は 突如§r §o球体に 変化すると 高速で 回転し 襲いかかってきたという。§r §o宇宙人の 科学兵器では ないかという 噂もあるが 真相は§r §o不明。 テツノワダチ という名は かの奇書§r §oバイオレットブックに 記された 謎の物体から 命名されたと§r §oいわれる。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.ironbundle.name": "テツノツツミ",
"cobblemon.species.ironbundle.desc": "オカルト雑誌の 記事になった 太古の 文明が 生み出した ロボットに 形が 似ている。",
"cobblemon.species.ironbundle.desc": "オカルト雑誌の 記事になった 太古の 文明が 生み出した ロボットに 形が 似ている。 ”§oパルデア未確認ファイル No.10§r §o太古の文明が 生んだ ロボット!? 謎の物体 テツノツツミ§r §o奇書バイオレットブックに 書かれた 謎の物体が 名前の由来§r §oである。 デリバードに 似た 形状をしており 球体の装置から§r §o膨大な氷を 噴射し 雪原を 移動するという。 正体に 関しては§r §o多くの説が あるが 超古代文明の産物 という説が 有力。§r §o太古の文献に テツノツツミ らしき 機械を 設計し§r §o使役していたという 記録が 残っているのだ。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.ironhands.name": "テツノカイナ",
"cobblemon.species.ironhands.desc": "あるアスリートが 改造された 姿だと オカルト雑誌が スクープした サイボーグに 近い。",
"cobblemon.species.ironhands.desc": "あるアスリートが 改造された 姿だと オカルト雑誌が スクープした サイボーグに 近い。 ”§oパルデア未確認ファイル§r §oNo.05 その正体は サイボーグ!? 謎の物体 テツノカイナ§r §o世紀の奇書 バイオレットブックに 記された 鉄の腕と 呼ばれる§r §o謎の物体から 命名された。 自在に動く 掌で 高速で§r §o走っていた 大型車両を 投げ飛ばしたという。 テツノカイナの§r §o正体として 有力なのは 瀕死の重傷を 負った アスリートが§r §oサイボーグ化した という説である。 ただ 何故 ハリテヤマを§r §o模した姿に なったのかは 謎に つつまれているのだ。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.ironjugulis.name": "テツノコウベ",
"cobblemon.species.ironjugulis.desc": "ロボットに 恋した サザンドラの 子どもと オカルト雑誌が 取り上げた ポケモンに 似ている。",
"cobblemon.species.ironjugulis.desc": "ロボットに 恋した サザンドラの 子どもと オカルト雑誌が 取り上げた ポケモンに 似ている。 ”§oパルデア未確認ファイル§r §oNo.07 機械!? ポケモン!? 謎の物体 テツノコウベ§r §oパルデア某所にて 目撃された サザンドラに 似た 危険な§r §o飛行物体。 高エネルギーを 放射し 建造物を 徹底的に§r §o破壊すると 次の標的を 求めるように§r §o飛び去っていったという。 サザンドラの凶暴性と§r §o機械の冷酷さを あわせ持つため 機械と サザンドラの§r §oハイブリッドなのではと 噂される。 奇書§r §oバイオレットブックに 書かれた 謎の物体が 名前の由来§r §oである。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.ironmoth.name": "テツノドクガ",
"cobblemon.species.ironmoth.desc": "人類を 監視する UFOだと オカルト雑誌が 紹介した 未知の 物体に 似ている。",
"cobblemon.species.ironmoth.desc": "人類を 監視する UFOだと オカルト雑誌が 紹介した 未知の 物体に 似ている。 ”§oパルデア未確認ファイル No.08§r §o人類を監視する UFO!? 謎の物体 テツノドクガ§r §o世紀の奇書バイオレットブックに 登場する 謎の物体から§r §o命名された。 ウルガモスに 似た 飛行物体であり 上空から§r §o地上の人々を 追尾し 時に 襲いかかってくることが§r §oあるという。 攻撃的な性質を 持つ 理由として テツノドクガの§r §o正体は 宇宙人が あやつる UFOだという 説がある。§r §oテツノドクガを 使い 人類を 監視し 侵略の機会を§r §oうかがっているかもしれない。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.ironthorns.name": "テツノイバラ",
"cobblemon.species.ironthorns.desc": "10億年後の バンギラスと 怪しい 雑誌が 取り上げていた ポケモンに 似た 部分が ある。",
"cobblemon.species.ironthorns.desc": "10億年後の バンギラスと 怪しい 雑誌が 取り上げていた ポケモンに 似た 部分が ある。 ”§oパルデア未確認ファイル§r §oNo.06 10億年後の バンギラス!? 謎の物体 テツノイバラ§r §oパルデア某所 とある洞くつで 目撃された 謎の物体が§r §oテツノイバラ である。 バンギラスに 酷似しているが 極めて§r §o冷徹な性質を 持つとともに 無駄な争いを 好まないという。§r §o金属質の装甲や 高い知性から 10億年後の バンギラスと§r §o噂されている。 奇書 バイオレットブックに 記された 鉄の茨と§r §o呼ばれる 謎の物体が 名前の由来に なっている。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.frigibax.name": "セビエ",
"cobblemon.species.frigibax.desc": "背びれで 熱を 吸収して 氷エネルギーに 変換する。 高温ほど エネルギーが 溜まる。",
"cobblemon.species.arctibax.name": "セゴール",
@@ -5266,13 +5266,13 @@
"cobblemon.species.chiyu.name": "イーユイ",
"cobblemon.species.chiyu.desc": "多くの 争いの 火種となった 勾玉に 集まった 妬みが 炎を まとい ポケモンとなった。",
"cobblemon.species.roaringmoon.name": "トドロクツキ",
"cobblemon.species.roaringmoon.desc": "謎の 多い 探検記の中で トドロクツキという 名で 記された 生物の 可能性が ある。",
"cobblemon.species.roaringmoon.desc": "謎の 多い 探検記の中で トドロクツキという 名で 記された 生物の 可能性が ある。 ”§oパルデア未確認ファイル No.04§r §o原始の ボーマンダ!? §o謎の生物 トドロクツキ§r §o奇書スカーレットブックに 書かれた 謎の生物が 名前の由来に§r §oなっている。 その姿は ほかの地方で ボーマンダに 発生する§r §oとある現象の 結果に 酷似しているが 関連性は 不明。 羽毛を§r §oまき散らしながら 高速で 飛びまわり 獲物を 襲う。 その§r §o凶暴な性質は ボーマンダ以上と 考えられており§r §o遭遇することが あっても 絶対に 接触を 避けるべきである。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.ironvaliant.name": "テツノブジン",
"cobblemon.species.ironvaliant.desc": "オカルト雑誌の 記事に あった マッドサイエンティストの 発明と 近い 特徴が いくつかある。",
"cobblemon.species.ironvaliant.desc": "オカルト雑誌の 記事に あった マッドサイエンティストの 発明と 近い 特徴が いくつかある。 ”§oパルデア未確認ファイル§r §oNo.04 博士の 異常な発明!? 謎の物体 テツノブジン§r §o奇書バイオレットブックに 書かれた 謎の物体が 名前の由来に§r §oなっている。 最強の エスパーポケモンを 求める§r §oマッドサイエンティストに よって 造られた ロボットという§r §o説がある。 数少ない 目撃者からの 証言によるとサーナイトと§r §oエルレイド どちらにも見えるような 奇妙な姿を§r §oしており対峙する者は 光りかがやく刃で 躊躇なく 切り捨てる§r §o残酷さを 持っているという。§r” -謎のオカルト雑誌より引用",
"cobblemon.species.koraidon.name": "コライドン",
"cobblemon.species.koraidon.desc": "拳で 大地を 引き裂いたと 古い 探検記に 記された ツバサノオウの 正体らしい。",
"cobblemon.species.koraidon.desc": "拳で 大地を 引き裂いたと 古い 探検記に 記された §oツバサノオウ§rの 正体らしい。",
"cobblemon.species.miraidon.name": "ミライドン",
"cobblemon.species.miraidon.desc": "古い 書物に 名が ある テツノオロチらしい。 雷で 大地を 灰に 変えたという。",
"cobblemon.species.miraidon.desc": "古い 書物に 名が ある §oテツノオロチ§rらしい。 雷で 大地を 灰に 変えたという。",
"cobblemon.species.walkingwake.name": "ウネルミナモ",
"cobblemon.species.walkingwake.desc": "謎多き 凶暴な 生物。 古い 探検記に 記された 水の 怪物から 名付けられた。",
"cobblemon.species.ironleaves.name": "テツノイサハ",
@@ -6495,7 +6495,7 @@
"cobblemon.ui.pokedex.info.form.glalie-mega": "メガオニゴーリ",
"cobblemon.species.glalie-mega.desc": "砕けた 口から 凄まじい 冷気を 吐くと あたり 一帯は ホワイトアウトに 陥ってしまう。",
"cobblemon.ui.pokedex.info.form.salamence-mega": "メガボーマンダ",
"cobblemon.species.salamence-mega.desc": "より 凶暴に なって 育ての トレーナーにも 襲いかかるほど。別名 血に 濡れた 三日月。",
"cobblemon.species.salamence-mega.desc": "より 凶暴に なって 育ての トレーナーにも 襲いかかるほど。別名 §o血に 濡れた 三日月§r。",
"cobblemon.ui.pokedex.info.form.metagross-mega": "メガメタグロス",
"cobblemon.species.metagross-mega.desc": "メガシンカで 脳を 刺激され 勝利の ためには 手段を 選ばない 冷酷な 性格に。",
"cobblemon.ui.pokedex.info.form.latias-mega": "メガラティアス",
@@ -6537,7 +6537,7 @@
"cobblemon.ui.pokedex.info.form.meloetta": "ボイスフォルム",
"cobblemon.ui.pokedex.info.form.meloetta-pirouette": "ステップフォルム",
"cobblemon.ui.pokedex.info.form.diancie-mega": "メガディアンシー",
"cobblemon.species.diancie-mega.desc": "額で 眩く 輝く ダイヤは なんと 2000カラット。 別名 ロイヤルピンクプリンセス。",
"cobblemon.species.diancie-mega.desc": "額で 眩く 輝く ダイヤは なんと 2000カラット。 別名 §oロイヤルピンクプリンセス§r。",
"cobblemon.ui.pokedex.info.form.color-cyan": "青緑色",
"cobblemon.ui.pokedex.info.form.vivillon-wings-forsaken": "みすてられたもよう",
"cobblemon.ui.pokedex.info.form.snake-pattern-speed": "すばやさのもよう",
@@ -7374,7 +7374,7 @@
"cobblemon.ui.pokedex.info.form.alakazam": " ",
"cobblemon.species.voltorb-hisui.desc": "モンスターボールと 空似せし 謎の ポケモン。気 昂るほど 腹に 蓄えし電流を 解き放ち 大笑す。",
"cobblemon.species.eevee-gmax.desc": "キョダイマックス パワーで さらに もふもふした 首まわりの 綿毛で 敵を 包みこみ とりこにする。",
"cobblemon.species.articuno-galar.desc": "凍りついたかのように 体の 自由を 奪う ビームを 撃ちだす フリーザーの 名を もつ ポケモン。",
"cobblemon.species.articuno-galar.desc": "凍りついたかのように 体の 自由を 奪う ビームを 撃ちだす §oフリーザー§rの 名を もつ ポケモン。",
"cobblemon.ui.pokedex.info.form.wooper-paldea": "パルデアのすがた",
"cobblemon.species.venusaur-gmax.desc": "2本の ふとい ツルを 振りまわし 戦う。10階建ての ビルを かるく なぎ倒すほど パワフルだ。",
"cobblemon.ui.pokedex.info.form.blastoise": " ",
@@ -7493,10 +7493,10 @@
"cobblemon.ui.pokedex.info.form.articuno-galar": "ガラルのすがた",
"cobblemon.ui.pokedex.info.form.zapdos": " ",
"cobblemon.ui.pokedex.info.form.zapdos-galar": "ガラルのすがた",
"cobblemon.species.zapdos-galar.desc": "羽毛が 擦れるとき バチバチと 電気のような 音が することから サンダーと 呼ばれてきた。",
"cobblemon.species.zapdos-galar.desc": "羽毛が 擦れるとき バチバチと 電気のような 音が することから §oサンダー§rと 呼ばれてきた。",
"cobblemon.ui.pokedex.info.form.moltres": " ",
"cobblemon.ui.pokedex.info.form.moltres-galar": "ガラルのすがた",
"cobblemon.species.moltres-galar.desc": "伝説の とりポケモンの 1匹。 ファイヤーが 姿を 見せると 春が 訪れると 言われている。",
"cobblemon.species.moltres-galar.desc": "邪悪な オーラを 炎のように 燃え上がらせる その 姿から §oファイヤー§rと 名づけられた。",
"cobblemon.ui.pokedex.info.form.aerodactyl": " ",
"cobblemon.ui.pokedex.info.form.cyndaquil": " ",
"cobblemon.ui.pokedex.info.form.mewtwo": " ",
@@ -7611,7 +7611,7 @@
"cobblemon.species.orbeetle-gmax.desc": "脳を 中心に 巨大化。 圧倒的 知能と 膨大な サイコエネルギーを もっている。",
"cobblemon.species.sandaconda-gmax.desc": "砂袋も 超巨大に。 体の まわりに 渦巻く 砂は 100万トンを 超える。",
"cobblemon.species.centiskorch-gmax.desc": "キョダイマックスの パワーによって 体温は 摂氏1000度を 超え 熱波で 相手を 焼き焦がす。",
"cobblemon.species.urshifu-gmax.desc": "怒りの化身 と 呼ばれている。 凄まじい 形相と 雄叫びで 世界の 邪気を 払うという。",
"cobblemon.species.urshifu-gmax.desc": "§o怒りの化身§r と 呼ばれている。 凄まじい 形相と 雄叫びで 世界の 邪気を 払うという。",
"cobblemon.cosmetic_item.give": "%1$sに%2$sを身に付けさせた!",
"cobblemon.cosmetic_item.take": "%2$sから %1$sを 受け取った!",
"cobblemon.cosmetic_item.already_wearing": "%1$sはもう %2$sを 身に付けている!",
@@ -7774,7 +7774,7 @@
"cobblemon.move.gmaxsweetness.desc": "キョダイマックスした タルップルが 繰りだす くさタイプの 攻撃。 味方の 状態異常を 回復する。",
"cobblemon.move.gmaxvoltcrash.desc": "キョダイマックスした ピカチュウが 繰りだす でんきタイプの 攻撃。 相手を まひ状態に する。",
"cobblemon.ui.pokedex.info.form.samurott-hisui": "ヒスイのすがた",
"cobblemon.species.dialga-origin.desc": "神と畏れし伝説 古の地に あり。 誕生により うつし世の 壁 消え去り 延々と 際限なき空 出現したり。",
"cobblemon.species.dialga-origin.desc": "光明 フォルムチェンジの 引き金。 創造神に 酷似する姿こそ 真なりと 思わざるを得ぬほどの 剛力 発揮す。",
"cobblemon.move.gmaxtartness.desc": "キョダイマックスした アップリューが 繰りだす くさタイプの 攻撃。 相手の 回避率を 下げる。",
"cobblemon.species.basculin-white.desc": "極寒の 海で 群れを 作り エサを 分け合って 暮らす。 時が 来ると 生まれた 川に 帰る。",
"cobblemon.species.landorus-therian.desc": "霊獣の姿にて 天翔けしとき 大地に 豊穣もたらす。 衆人 これを 敬う。 一刻あれば 古の地を 縦断す。",
@@ -8179,8 +8179,8 @@
"cobblemon.ui.pokedex.info.form.eiscue-noiceface": "ナイスフェイス",
"cobblemon.pokedex.variation.face_spot_1": "顔のブチ1",
"cobblemon.pokedex.variation.face_spot_2": "顔のブチ2",
"cobblemon.species.zacian-crowned.desc": "あらゆるものを 切り捨てるさまから 妖精王の剣 と 呼ばれ 敵味方に 恐れ崇められた。",
"cobblemon.species.zamazenta-crowned.desc": "いかなる 攻撃も 弾き返す 姿は 格闘王の盾 と 呼ばれ 恐れ 崇められた。",
"cobblemon.species.zacian-crowned.desc": "あらゆるものを 切り捨てるさまから §o妖精王の剣§r と 呼ばれ 敵味方に 恐れ崇められた。",
"cobblemon.species.zamazenta-crowned.desc": "いかなる 攻撃も 弾き返す 姿は §o格闘王の盾§r と 呼ばれ 恐れ 崇められた。",
"cobblemon.ui.pokedex.info.form.calyrex-ice": "はくばじょう",
"cobblemon.species.gimmighoul-roaming.desc": "古い コインを 背負い さまよう。 コインを 拾おうとした 人の 生気を 吸い取って 生きている。",
"cobblemon.species.archaludon.desc": "まわりから 静電気を 集める。 四つん這いの 体勢で 撃ち出す ビームは 絶大な 威力。",
@@ -9466,7 +9466,7 @@
"cobblemon.species.victreebel-mega.desc": "メガシンカにより 増量した 溶解液が 口内を 満たす。 気を抜くと 口から 溢れだす。",
"cobblemon.species.starmie-mega.desc": "動きが 人間らしくなってきた。 意思疎通を 図ろうとしているのか 人に 成り代わるつもりかは 謎。",
"cobblemon.species.dragonite-mega.desc": "メガシンカのエネルギーが 優しい 気持ちを 過剰に パワーアップ。 慈悲の 心で 敵を 葬る。",
"cobblemon.species.meganium-mega.desc": "4輪の 花から 放たれる ソーラービームは 威力絶大。 別名 メガソーラー砲。",
"cobblemon.species.meganium-mega.desc": "4輪の 花から 放たれる ソーラービームは 威力絶大。 別名 §oメガソーラー砲§r。",
"cobblemon.species.feraligatr-mega.desc": "フードのような ヒレと 両腕で 巨大な 顎を 作り 噛みつく 威力は 10倍 アップした。",
"cobblemon.species.skarmory-mega.desc": "飛行速度は 音速を 超える。 衝撃波を 巻き起こして 敵を 吹き飛ばし ツメで とどめを 刺す。",
"cobblemon.species.chimecho-mega.desc": "全身を 震わせて あらゆる 方向を 音波で 攻撃。 聴いていると 自分を 見失う。",
@@ -2920,7 +2920,7 @@
"cobblemon.move.meteorbeam": "메테오빔",
"cobblemon.move.meteorbeam.desc": "1턴째에 우주의 힘을 모아서 특수공격을 올리고 2턴째에 상대를 공격한다.",
"cobblemon.move.meteormash": "코멧펀치",
"cobblemon.move.meteormash.desc": "혜성과 같은 펀치를 날려서 상대를 공격한다. 20%의 확률로 자신의 공격이 2랭크 오른다.",
"cobblemon.move.meteormash.desc": "혜성과 같은 펀치를 날려서 상대를 공격한다. 20%의 확률로 자신의 공격이 1랭크 오른다.",
"cobblemon.move.metronome": "손가락흔들기",
"cobblemon.move.metronome.desc": "손가락을 흔들어 자신의 뇌를 자극해서 모든 기술 중 어느 하나를 날린다.",
"cobblemon.move.milkdrink": "우유마시기",
@@ -5699,7 +5699,7 @@
"item.cobblemon.blunder_policy.tooltip_2": "사용 시 소모된다",
"item.cobblemon.cell_battery.tooltip_1": "지니게 하고 전기 기술을 받으면 공격이 올라간다",
"item.cobblemon.cell_battery.tooltip_2": "사용 시 소모된다",
"item.cobblemon.charcoal_stick.tooltip": "물건을 태우는 연료. 지니게 하면 불꽃건을 태우는 연료. 지니게 하면 불꽃타입 기술의 위력이 올라간다",
"item.cobblemon.charcoal_stick.tooltip": "물건을 태우는 연료. 지니게 하면 불꽃타입 기술의 위력이 올라간다",
"item.cobblemon.choice_band.tooltip": "기술을 구애받는 머리띠. 지니게 하면 공격은 올라가지만 똑같은 기술밖에 쓰지 못한다",
"item.cobblemon.choice_scarf.tooltip": "기술을 구애받는 스카프. 지니게 하면 스피드는 올라가지만 같은 기술밖에 쓸 수 없다",
"item.cobblemon.cleanse_tag.tooltip": "효과가 없다",
@@ -9408,9 +9408,9 @@
"cobblemon.ui.pokedex.info.form.glimmora-mega": "Mega Glimmora",
"cobblemon.ui.pokedex.info.form.tatsugiri-mega": "Mega Tatsugiri Forma Curvada",
"cobblemon.species.tatsugiri-mega.desc": "O cérebro de Tatsugiri foi energizado pela Mega Evolução, tornando-o mais astuto. Pode criar e comandar cópias de si mesmo.",
"cobblemon.ui.pokedex.info.form.tatsugiri-droopymega": "Mega Tatsuguri Forma Molenga",
"cobblemon.ui.pokedex.info.form.tatsugiri-droopymega": "Mega Tatsuguri Forma Pendular",
"cobblemon.species.tatsugiri-droopymega.desc": "Solidifica a energia da Mega Evolução, construindo uma pilha excessiva para lançar como projéteis. Esses projéteis explodem ao primeiro contato.",
"cobblemon.ui.pokedex.info.form.tatsugiri-stretchymega": "Mega Tatsugiri Forma Elástica",
"cobblemon.ui.pokedex.info.form.tatsugiri-stretchymega": "Mega Tatsugiri Forma Estendida",
"cobblemon.species.tatsugiri-stretchymega.desc": "Usando a energia da Mega Evolução, cria um prato onde repousar, permitindo com que se mova com mobilidade total até mesmo pelo ar.",
"cobblemon.ui.pokedex.info.form.baxcalibur-mega": "Mega Baxcalibur",
"cobblemon.species.baxcalibur-mega.desc": "A lâmina dorsal de Baxcalibur ficou ainda maior graças à Mega Evolução. Esse Pokémon dispara feixes do cabo localizado em seu peito."
@@ -3500,15 +3500,15 @@
"cobblemon.species.machamp.name": "Мачамп",
"cobblemon.species.machamp.desc": "Мачамп известен как покемон, в совершенстве овладевший всеми видами боевых искусств. Если он схватит противника своими четырьмя руками, битву можно считать практически законченной. Несчастный противник будет отброшен далеко за горизонт.",
"cobblemon.species.bellsprout.name": "Беллспраут",
"cobblemon.species.bellsprout.desc": "Prefers hot and humid places. It ensnares tiny bugs with its vines and devours them.",
"cobblemon.species.bellsprout.desc": "Тонкое и гибкое тело Беллспраута позволяет ему изгибаться и раскачиваться, чтобы уклоняться от любой атаки, какой бы сильной она ни была. Этот покемон извергает из своего рта едкую жидкость, способную растворить даже железо.",
"cobblemon.species.weepinbell.name": "Випинбелл",
"cobblemon.species.weepinbell.desc": "When hungry, it swallows anything that moves. Its hapless prey is dissolved by strong acids.",
"cobblemon.species.weepinbell.desc": "На задней части тела Випинбелла есть большой крючок. Ночью этот покемон цепляется к ветке дерева и засыпает. Если он шевелится во сне, то, проснувшись, может обнаружить, что он оказался на земле.",
"cobblemon.species.victreebel.name": "Виктрибелл",
"cobblemon.species.victreebel.desc": "Lures prey with the sweet aroma of honey. Swallowed whole, the prey is dissolved in a day, bones and all.",
"cobblemon.species.victreebel.desc": "У Виктрибела из головы растёт длинная лиана. Он машет ею и взмахивает, имитируя животное, чтобы привлечь добычу. Когда ничего не подозревающая добыча подходит поближе, этот покемон проглатывает её целиком.",
"cobblemon.species.tentacool.name": "Тентакул",
"cobblemon.species.tentacool.desc": "They fire beams from the glassy, magenta orbs that resemble eyes atop their heads, and they drift in shallow seas. During low tide, they can sometimes be found on beaches, desiccated.",
"cobblemon.species.tentacool.desc": "Тентакул поглощает солнечный свет и преломляет его, используя воду, находящуюся внутри его тела, чтобы преобразовать его в лучевую энергию. Этот покемон стреляет лучами из небольшого круглого органа, расположенного над глазами.",
"cobblemon.species.tentacruel.name": "Тентакрул",
"cobblemon.species.tentacruel.desc": "It has 80 tentacles, each with a venomous tip. These tentacles are also extendible, lengthening when Tentacruel attempts to catch prey. Use caution.",
"cobblemon.species.tentacruel.desc": "У Тентакрула есть щупальца, которые он может свободно удлинять и укорачивать по собственному желанию. Он опутывает добычу своими щупальцами и ослабляет её, впрыскивая в неё сильный яд. Он может поймать до 80 особей одновременно.",
"cobblemon.species.geodude.name": "Геодуд",
"cobblemon.species.geodude.desc": "Makes its home in mountainous regions, using its arms to climb along harsh mountain roads. Can be troublesome—carelessly kicking one will cause it to fly into a rage and chase after you.",
"cobblemon.species.graveler.name": "Гравелер",
@@ -8392,7 +8392,7 @@
"item.cobblemon.sweet_heart": "Сладкое сердце",
"item.cobblemon.sweet_heart.tooltip": "Сладкое шоколадное лакомство",
"item.cobblemon.technical_machine": "Техническое устройство (ТУ)",
"item.cobblemon.blank_tm": "Пустое ТУ",
"item.cobblemon.blank_tm": "Пустая ТМ",
"block.cobblemon.deepslate_crystal_core": "Глубинный сланец с кристальным ядром",
"block.cobblemon.normal_gem_block": "Блок нормального самоцвета",
"block.cobblemon.fire_gem_block": "Блок огненного самоцвета",
@@ -8432,16 +8432,16 @@
"block.cobblemon.fairy_gem_cluster": "Друза волшебного самоцвета",
"cobblemon.item_class.technical_machine": "Техническое устройство",
"block.cobblemon.damaged_monitor": "Повреждённый отслеживатель данных",
"block.cobblemon.tm_machine": "ТУ станок",
"block.cobblemon.tm_machine": "ТМ станок",
"block.cobblemon.disc_shelf": "Полка для дисков",
"cobblemon.fishing_bait_effects.mark_chance.tooltip": "%1$s%% - +%3$s%% шанс метки",
"cobblemon.ui.sort.discovered": "Сортировать по статусу открытия",
"cobblemon.fishing_bait_effects.alpha_chance.tooltip": "%1$s%% - привлекает альфа покемонов",
"cobblemon.fishing_bait_effects.size.tooltip": "%1$s%% - меняет размер на %3$s%%",
"cobblemon.technical_machine_effect_header": "О техническом устройстве:",
"cobblemon.command.technicalmachine.invalid-tm": "Неправильное ТУ",
"cobblemon.command.technicalmachine.invalid-tm": "Неправильная ТМ",
"cobblemon.ui.generic.question_marks": "???",
"cobblemon.container.tm_machine": "ТУ станок",
"cobblemon.container.tm_machine": "ТМ станок",
"cobblemon.ui.edit.habitat": "Редактор среды",
"cobblemon.ui.edit.habitat.mimic_id": "Идентификатор копируемого блока",
"cobblemon.ui.edit.habitat.phase_order": "Фаза порядка: %s",
@@ -8567,20 +8567,20 @@
"advancements.cobblemon.construct_note_block_sequencer.description": "Создайте аудиосеквенсор из нотного блока и полки для дисков",
"advancements.cobblemon.download_porygon": "Технические неполадки",
"advancements.cobblemon.download_porygon.description": "Вы же не будете скачивать Поригона",
"cobblemon.ui.moves.learnset.tm.discovered": "Обнаружен",
"cobblemon.ui.moves.learnset.tm.discovered": "Обнаружена",
"cobblemon.ui.moves.level": "Уровень",
"cobblemon.ui.moves.egg": "Яйцо",
"cobblemon.ui.moves.tm": "ТУ",
"cobblemon.ui.tm_machine.can_learn": "Выучиваемый",
"cobblemon.ui.tm_machine.cannot_learn": "Не выучиваемый",
"cobblemon.ui.tm_machine.learned": "Выучен",
"cobblemon.ui.moves.tm": "ТМ",
"cobblemon.ui.tm_machine.can_learn": "Выучиваемая",
"cobblemon.ui.tm_machine.cannot_learn": "Не выучиваемая",
"cobblemon.ui.tm_machine.learned": "Выучена",
"cobblemon.tms.unknown_move": "Неизвестный приём",
"cobblemon.tms.already_known": "%1$s уже знает %2$s!",
"cobblemon.tms.teach_move": "%1$s выучивает %2$s!",
"cobblemon.tms.cannot_learn": "%1$s не может выучить %2$s!",
"cobblemon.tms.unlock_tm": "Вы разблокировали ТУ для %1$s!",
"cobblemon.tms.new_tms_learned": "Выучены новые ТУ!",
"cobblemon.tms.check_tmm": "Проверьте ТУ станок",
"cobblemon.tms.unlock_tm": "Вы разблокировали ТМ для %1$s!",
"cobblemon.tms.new_tms_learned": "Выучены новые ТМ!",
"cobblemon.tms.check_tmm": "Проверьте ТМ станок",
"cobblemon.ui.party.party_select_title": "Изменить выбор команды",
"cobblemon.ui.party.party_select_description": "Нажмите %1$s/%2$s или удерживайте %3$s и прокрутите.",
"cobblemon.ui.category": "Категория",
@@ -8592,7 +8592,7 @@
"cobblemon.ui.pokedex.filter.seen": "Просмотрен",
"cobblemon.ui.pokedex.filter.unregistered": "Незарегестрирован",
"cobblemon.ui.pokedex.filter.rideable": "Ездовой",
"cobblemon.ui.pokedex.filter.undiscovered_tm_move": "Необнаруженный ТУ приём",
"cobblemon.ui.pokedex.filter.undiscovered_tm_move": "Необнаруженный ТМ приём",
"cobblemon.habitat.abandoned_fortress.name": "Заброшенная крепость",
"cobblemon.habitat.abandoned_village_house.name": "Заброшенный деревенский дом",
"cobblemon.habitat.ancient_wellspring.name": "Древний источник",
@@ -8641,16 +8641,16 @@
"cobblemon.habitat.volcanic_plume.name": "Вулканический шлейф",
"cobblemon.habitat.warped_fungal_dwelling.name": "Искажённое грибное жилище",
"cobblemon.habitat.zen_garden.name": "Сад дзен",
"cobblemon.subtitle.tm.use": "Использовано техническое устройство",
"cobblemon.subtitle.block.tm_machine.burn_loop": "ТУ станок печатает ТУ",
"cobblemon.subtitle.block.tm_machine.start": "ТУ станок начинает печатать ТУ",
"cobblemon.subtitle.block.tm_machine.craft": "ТУ станок заканчивает печатать ТУ",
"cobblemon.subtitle.block.tm_machine.open": "Крышка ТУ станка открывается",
"cobblemon.subtitle.block.tm_machine.close": "Крышка ТУ станка закрывается",
"cobblemon.subtitle.block.tm_machine.tm_insert": "Пустое ТУ вставлено в ТУ станок",
"cobblemon.subtitle.block.tm_machine.tm_retrieve": "ТУ извлечено из ТУ станка",
"cobblemon.subtitle.block.tm_machine.item_insert": "Предмет вставлен в ТУ станок",
"cobblemon.subtitle.block.tm_machine.item_retrieve": "Предмет извлечён из ТУ станка",
"cobblemon.subtitle.tm.use": "Использована техническая машина",
"cobblemon.subtitle.block.tm_machine.burn_loop": "ТМ станок печатает ТМ",
"cobblemon.subtitle.block.tm_machine.start": "ТМ станок начинает печатать ТМ",
"cobblemon.subtitle.block.tm_machine.craft": "ТМ станок заканчивает печатать ТМ",
"cobblemon.subtitle.block.tm_machine.open": "Крышка ТМ станка открывается",
"cobblemon.subtitle.block.tm_machine.close": "Крышка ТМ станка закрывается",
"cobblemon.subtitle.block.tm_machine.tm_insert": "Пустая ТМ вставлена в ТМ станок",
"cobblemon.subtitle.block.tm_machine.tm_retrieve": "ТМ извлечена из ТМ станка",
"cobblemon.subtitle.block.tm_machine.item_insert": "Предмет вставлен в ТМ станок",
"cobblemon.subtitle.block.tm_machine.item_retrieve": "Предмет извлечён из ТМ станка",
"cobblemon.ui.exp.number": "+%1$s §o опыта",
"cobblemon.config.ui.award_experience_to_fainted_pokemon": "Выдавать опыт поверженным покемонам",
"cobblemon.config.ui.award_experience_to_fainted_pokemon.tooltip": "Поверженные покемоны получают опыт за противников, которых они помогли победить до того, как были повержены.",
@@ -8687,9 +8687,9 @@
"cobblemon.config.ui.minimum_riding_scale": "Минимальный модификатор размера покемона для езды",
"cobblemon.config.ui.minimum_riding_scale.tooltip": "Размер, при котором нельзя кататься на покемоне.",
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default": "Разблокировать все приёмы",
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default.tooltip": "Если эта опция включена, ТУ и приёмы, получаемые при повышении уровня, становятся доступными и можно их найти в ТУ машине и в разделе покедекса для приёмов.",
"cobblemon.config.ui.infinite_tm_uses": "Бесконечное использование ТУ",
"cobblemon.config.ui.infinite_tm_uses.tooltip": "ТУ не пропадают после использования.",
"cobblemon.config.ui.unlock_all_move_dex_moves_by_default.tooltip": "Если эта опция включена, ТМ и приёмы, получаемые при повышении уровня, становятся доступными и можно их найти в ТМ машине и в разделе с приёмами в покедексе.",
"cobblemon.config.ui.infinite_tm_uses": "Бесконечное использование ТМ",
"cobblemon.config.ui.infinite_tm_uses.tooltip": "ТМ не пропадают после использования.",
"cobblemon.battle.combine": "Два приёма стали одним! Это совместный приём!",
"cobblemon.battle.waiting": "%1$s ожидает приёма от %2$s...",
"cobblemon.battle.start.alphaboost": "Альфа %1$s наполняется дикой силой! Его сила возрастает!",
@@ -9013,5 +9013,13 @@
"cobblemon.species.growlithe-hisui.desc": "Они патрулируют свою территорию парами. Я полагаю, что наличие магматических пород в шерсти этого вида является результатом вулканической деятельности в его среде обитания.",
"cobblemon.species.arcanine-hisui.desc": "Набрасывается на своих врагов с окутанными ярким пламенем клыками. Несмотря на свои внушительные размеры, он ловко делает манёвры во всех стороны, увлекая противников в обманчиво весёлую погоню, словно танцуя вокруг них.",
"cobblemon.species.alakazam-mega.desc": "Потеряв свои мышцы, истинную мощь Алаказама была высвобождена. Используя свои психические способности он может предвидеть всё.",
"cobblemon.species.machamp-gmax.desc": "Гигантамакс энергия, текущая в его руках, придаёт его ударам силу, сравнимую с взрывом бомбы."
"cobblemon.species.machamp-gmax.desc": "Гигантамакс энергия, текущая в его руках, придаёт его ударам силу, сравнимую с взрывом бомбы.",
"cobblemon.ui.starter.random": "Случайный",
"advancements.cobblemon.obtain_tm": "Покажи, что ты можешь",
"cobblemon.ui.starter.random_description": "Случайный покемон из списка доступных вариантов. Невозможно узнать, кто им будет, пока не выбрана эта опция.",
"cobblemon.config.ui.world_spawning_blocklist": "Список запрещённых появлений на мир",
"cobblemon.config.ui.world_spawning_blocklist.tooltip": "Миры, в которых отключается принцип появления покемонов.",
"advancements.cobblemon.obtain_tm.description": "Найдите или напечатайте ТМ",
"cobblemon.species.victreebel-mega.desc": "В результате Мега эволюции объём кислоты у этого покемона увеличился, заполняя весь его рот. Если он не будет осторожен, кислота переполнится и выльется наружу.",
"cobblemon.ui.pokedex.info.form.victreebel-mega": "Мега Виктрибелл"
}
@@ -7347,5 +7347,6 @@
"cobblemon.mark.ribbon_day_downcast": "Стрічка смутку",
"cobblemon.mark.ribbon_day_downcast.desc": "Стрічка на згадку про почуття смутку, що додали життю барв.",
"cobblemon.mark.ribbon_day_downcast.title": "Приголомшений %1$s",
"cobblemon.mark.ribbon_day_relax": "Стрічка відпочинку"
"cobblemon.mark.ribbon_day_relax": "Стрічка відпочинку",
"cobblemon.mark.ribbon_day_relax.title": "%1$s відпочив"
}
@@ -0,0 +1,6 @@
{
"parent": "minecraft:item/generated",
"textures": {
"layer0": "cobblemon:item/poke_balls/strange_ball"
}
}

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