Compare commits

...
Author SHA1 Message Date
Gitoido Okaski 1b52b3d517 feat: the strange goat (datapacked) 2026-10-28 19:27:41 +03:00
Gitoido Okaski 82b5d12f32 feat+refactor: ball registry sync, also broke all the rods guh 2026-10-28 19:27:27 +03:00
Gitoido Okaski 07c5d10f3d feat+refactor: very cursed model loading 2026-10-28 19:23:03 +03:00
Gitoido Okaski 5ac50e0931 refactor: updated model paths for already datapacked balls 2026-10-28 19:20:00 +03:00
Gitoido Okaski 45079052de refactor: rolled balls to a new place 2026-10-28 19:19:02 +03:00
Gitoido Okaski 76a9feb7d9 feat: not so simple balls datapacked 2026-10-28 14:12:11 +03:00
Gitoido Okaski 33faecc517 refactor: also fix a parameter name 2026-10-28 13:51:58 +03:00
Gitoido Okaski 205323d568 refactor: on a second thought, lets use simple modifier here 2026-10-28 13:51:18 +03:00
Gitoido Okaski c6dfb77e47 feat: simple balls datapacked 2026-10-28 13:47:43 +03:00
Gitoido Okaski 2273e32d9f perf: bail early from ExpressionModifier if default query used 2026-10-28 12:52:36 +03:00
Gitoido Okaski ed159cf9e5 feat: add stack to pokeball molang struct 2026-10-28 12:40:18 +03:00
Gitoido Okaski d36f69b63d Merge remote-tracking branch 'origin/feat/datapackable-pokeballs' into feat/datapackable-pokeballs 2026-10-28 12:27:54 +03:00
Gitoido aa2eed1f60 feat+refactor: add separate molang script modifier and rename og molang modifier to expression 2026-10-28 12:27:24 +03:00
Gitoido 7abb786157 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-28 12:27:24 +03:00
Gitoido 488a89e3f1 chore: cleanup PokeBallComponent 2026-10-28 12:27:24 +03:00
Gitoido 093deb73fb refactor: caught ball fallback to strange ball in case of datapack removal 2026-10-28 12:27:24 +03:00
Gitoido 792ce4d0cb feat: shedders evo custom ball support 2026-10-28 12:27:24 +03:00
Gitoido fd004cc51b refactor: default to strange ball instead of poke ball for some cases. code cleanup 2026-10-28 12:27:24 +03:00
Gitoido 015c072a41 feat: strange ball i18n 2026-10-28 12:27:24 +03:00
Gitoido cb389f6cef refactor: update item renderer and model loader mixins to account for custom balls 2026-10-28 12:27:24 +03:00
Gitoido 3f1ff0503d feat: recall beam now uses correct beam color for ball 2026-10-28 12:27:24 +03:00
Gitoido fcf8c44aca feat: molang effects - functional MVP is complete 2026-10-28 12:27:24 +03:00
Gitoido c128a435c7 feat: very very very temporary assets for testing purposes 2026-10-28 12:27:24 +03:00
Gitoido 8bd20692f4 feat+refactor: add MoLangModifier, remove deprecated poke ball getters 2026-10-28 12:27:24 +03:00
Gitoido 83c019f566 fix: add strange ball to poke_balls item tag 2026-10-28 12:27:24 +03:00
Gitoido 49047cac05 refactor: use correct ball name in battle log 2026-10-28 12:27:24 +03:00
Gitoido 39c40bfebb fix: drop correct pokeball stack on wall hit/drop 2026-10-28 12:27:24 +03:00
Gitoido beed33ccb0 refactor: strange ball defaults for EmptyPokeBallEntity 2026-10-28 12:27:24 +03:00
Gitoido 4d8a6c8c12 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-28 12:27:24 +03:00
Gitoido ffd0ef499d fix: default value for pokeball root bone in its posable state 2026-10-28 12:27:24 +03:00
Gitoido 56fd26d29e refactor: derive poke ball name from component or lack of thereof 2026-10-28 12:27:24 +03:00
Gitoido bc908a9a0a 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-28 12:27:24 +03:00
Gitoido c5a7b4bc06 feat+refactor: introduce CatchRateModifierAdapter and update basic modifiers to support serialization 2026-10-28 12:27:24 +03:00
Gitoido b4b55967bd refactor: better way to derive root bone of pokeball model 2026-10-28 12:27:24 +03:00
Gitoido ca52c578dd fix: mixed up npc and pokeball poser directories 2026-10-28 12:27:24 +03:00
Gitoido 7d78a72925 refactor: step 2 - lets get rid of hardcoded posers 2026-10-28 12:27:23 +03:00
Gitoido f4cea63612 refactor: step 1 - use itemStack 2026-10-28 12:27:23 +03:00
Hiroku f4140a097d Put the snap back on 2026-10-27 20:16:12 +10:00
Hiroku b79dbcca80 no scope. I mean no snap 2026-10-27 19:42:59 +10:00
Hiroku 5ac12a897a Merge remote-tracking branch 'origin/lang-updates' 2026-10-27 19:28:28 +10:00
Polymeta ae5f7c83f2 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2507
2026-10-26 23:10:50 +00:00
ewwww f14de9ab58 updated water shuriken 2026-10-26 16:35:00 -04:00
ewwww 8a1c846c2c Merge branch 'main' into implement_pokemon 2026-10-26 16:06:28 -04:00
Polymeta 459e7d3490 this doesnt make any sense
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-26 09:16:39 +00:00
Polymeta 5a9be8944f Merge branch 'hotbox-hitfix' into 'main'
Orb Ceiling Hitbox Correction

See merge request cable-mc/cobblemon!2512
2026-10-26 07:09:59 +00:00
Polymeta 3bb941b087 Merge branch 'dusclops-cry-fix' into 'main'
fixed dusclops using squirtle's cry

See merge request cable-mc/cobblemon!2511
2026-10-26 07:07:40 +00:00
dhi_holo 384b496d76 T H E O R B 2026-10-26 13:37:09 +10:00
geumu 4caa736e0b Translated using Weblate (Korean)
Currently translated at 99.6% (9549 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-10-26 02:38:47 +00:00
geumu c3e9051918 Translated using Weblate (Korean)
Currently translated at 98.5% (9445 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ko/
2026-10-26 02:26:52 +00:00
ewwww fe940caafc fixed dusclops using squirtle's cry 2026-10-25 21:33:38 -04:00
Polymeta c9d148ec9c bump showdown build and changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-26 00:41:23 +00:00
Polymeta e611bf4a77 Merge branch 'fix-missing-recipe-book-data' into 'main'
Fixed Cooking Pot recipe book tabs and entries on Fabric

See merge request cable-mc/cobblemon!2510
2026-10-26 00:37:47 +00:00
Polymeta 8093822140 Merge branch 'battle-selection-fix' into 'main'
Fixed a bug with battle selection.

See merge request cable-mc/cobblemon!2509
2026-10-26 00:27:00 +00:00
Fyre520 f695ec15f9 Fixed Cooking Pot recipe book tabs and entries on Fabric 2026-10-26 02:01:15 +05:30
Deltric 1f6c791ce6 Fixed a bug with battle selection. 2026-10-25 14:24:44 -04:00
Polymeta 570805fdf1 Merge branch 'structure-tags-for-spawning' into 'main'
Structure Spawn Tags

See merge request cable-mc/cobblemon!2508
2026-10-25 16:53:38 +00:00
dhi_holo ddecb08bc7 Edit is_illager_structure.json 2026-10-26 00:21:36 +10:00
Kenji_64 a1b930a21f More Frogadier fixes (ty Mitchoo) 2026-10-25 14:14:44 +01:00
Kenji_64 7728ff3892 Fixed Fearow missing blink crashing during gliding 2026-10-25 14:11:22 +01:00
阿鱼 0b38e063d2 Translated using Weblate (Chinese (Simplified Han script))
Currently translated at 98.7% (9464 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/zh_Hans/
2026-10-25 10:52:25 +00:00
Hiroku 4d7cfcdf61 Added q.target_fainted to MoveInstruction 2026-10-25 11:11:52 +10:00
Hiroku e56dd6ef64 snap it back on 2026-10-25 10:43:01 +10:00
Kenji_64 236b98fa78 I forgot the changelog :( 2026-10-25 02:21:08 +02:00
Kenji_64 ac61e0d4d0 Had to fix Froakie again. Heck 2026-10-25 02:20:20 +02:00
Kenji_64 d8956fd66f Froakie line eye lid and animation fixes 2026-10-25 02:14:01 +02:00
Hiroku ffe005511d but what would snapshot look like when it's false? 2026-10-25 10:03:22 +10:00
Hiroku 1e634a20b8 Merge remote-tracking branch 'origin/lang-updates' 2026-10-25 09:57:49 +10:00
ewwww 50a5a78dd4 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-10-24 19:16:59 -04:00
ewwww 76c2654399 it appears my lang tweaks were reverted somehow 2026-10-24 19:16:20 -04:00
Polymeta f9f4745e93 Merge branch 'implement_pokemon' into 'main'
Some recent riding stuff adjustments

See merge request cable-mc/cobblemon!2506
2026-10-24 16:23:07 +00:00
Kenji_64 c700674289 Fearow animations updated (again) and added all placeholders I made to the changelog 2026-10-24 18:05:05 +02:00
Kenji_64 c4f0032eee Fixed Squawkabilly's missing toe 2026-10-24 15:24:17 +02:00
Kenji_64 d92986016b Adjusted Fearow's ride stats further 2026-10-24 02:12:39 +02:00
Kenji_64 ab57e01242 Fearow is now rideable with temporary animations 2026-10-24 01:46:41 +02:00
Kenji_64 0da1c740a4 Adjusted Altaria ride stats 2026-10-24 01:44:26 +02:00
Kenji_64 a7979aa80c Adjusted Togekiss ride stats. Changed style from jet to bird 2026-10-24 01:44:10 +02:00
Polymeta 59804a4e08 rescrape spawns and drops
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-23 23:36:25 +01:00
Polymeta 072c67acad Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2496
2026-10-23 22:30:54 +00:00
ewwww 60bbe221de tweaked vikavolt's jump stat 2026-10-23 17:51:10 -04:00
ewwww 5f06dc214b added vikavolt ride stats 2026-10-22 22:51:05 -04:00
ewwww a6607e585b changed vikavolt to bird style 2026-10-22 21:45:56 -04:00
Gitoido Okaski 20584f4507 Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-10-23 01:02:35 +03:00
Kenji_64 9660f1f724 Fixed Appletun having a green tongue. (Likely from food coloring...) 2026-10-23 00:31:07 +03:00
Kenji_64 c8cbace800 Fixed Fearow's model being messed up and added the head_ai back 2026-10-23 00:31:07 +03:00
Kenji_64 a616a24923 Fixed Swampert's weird head_ai 2026-10-23 00:31:07 +03:00
Gitoido Okaski 12035dd589 chore: mudkip animation call fix, resolver orderings streamlined 2026-10-23 00:31:07 +03:00
Kenji_64 9627b80b01 Added Ninetales + alolan placeholder animations 2026-10-23 00:31:07 +03:00
Kenji_64 4e9cecc854 Converted Squawkabilly's poser to JSON and added placeholder animations 2026-10-23 00:31:06 +03:00
ewwww 9d4b19b185 vikavolt can be mounted (stat placeholders) 2026-10-23 00:31:06 +03:00
Kenji_64 aee96d06aa Made Pichu shut up in its sleep 2026-10-23 00:31:06 +03:00
Kenji_64 23e3c585f7 Wave 2 of profile adjustments 2026-10-23 00:31:06 +03:00
Kenji_64 c22a88079f Added Fearow placeholders and fixed its neck (also adjusted Swampert placeholder) 2026-10-23 00:31:06 +03:00
Kenji_64 2225402082 Added transparency to Vikavolt 2026-10-23 00:31:06 +03:00
Kenji_64 9197207431 Implemented Magby and Magmar animations 2026-10-23 00:31:06 +03:00
Kenji_64 47c7f7079c Added placeholder animations to Mudkip, Marshtomp, Swampert and Spearow 2026-10-23 00:31:06 +03:00
Kenji_64 eb0c1dddfd Scaled down a huge lot of small Pokémon's profiles and fixed some that were off over all 2026-10-23 00:31:06 +03:00
Kenji_64 e3dbfe6ff8 Added Eerie Spell activate and Hospitality lang (Hospitality's text does not work properly atm due to a Doubles bug) 2026-10-23 00:31:06 +03:00
whatsy fac207f36b Revert "Rescrape drops"
This reverts commit 6a7d5d2133.
2026-10-23 00:31:06 +03:00
ewwww 379e794848 removed igglybuff, jigglypuff, and wigglytuff's rights to float 2026-10-23 00:31:06 +03:00
ewwww 9081a4b131 added water shuriken fx 2026-10-23 00:31:06 +03:00
ewwww dc6d75b8ee sped up cacnea and hippowdon's walk speed 2026-10-23 00:31:06 +03:00
ewwww bb297a4448 fixed t-posing archeops during air_cry 2026-10-23 00:31:06 +03:00
whatsy a93922c438 Rescrape drops 2026-10-23 00:31:06 +03:00
Kenji_64 f7d21c80d7 Fixed Appletun having a green tongue. (Likely from food coloring...) 2026-10-22 23:16:45 +02:00
Kenji_64 9248d26fd4 Fixed Fearow's model being messed up and added the head_ai back 2026-10-22 23:03:50 +02:00
dhi_holo b5ac54cd16 Tweaks to prepare for Scrapes 2026-10-21 14:22:17 +10:00
dhi_holo dc703e67f7 Merge branch 'main' into structure-tags-for-spawning 2026-10-21 14:01:10 +10:00
Kenji_64 0930721865 Fixed Swampert's weird head_ai 2026-10-19 16:34:31 +02:00
Gitoido Okaski 644ff1772e chore: mudkip animation call fix, resolver orderings streamlined 2026-10-19 16:56:42 +03:00
Kenji_64 3c7e0bebb6 Added Ninetales + alolan placeholder animations 2026-10-19 15:44:42 +02:00
Kenji_64 0c0eea3876 Converted Squawkabilly's poser to JSON and added placeholder animations 2026-10-19 15:44:20 +02:00
ewwww 5535140bb7 vikavolt can be mounted (stat placeholders) 2026-10-19 03:37:39 -04:00
Kenji_64 9b0ca1c16b Made Pichu shut up in its sleep 2026-10-18 18:46:52 +02:00
Kenji_64 052ff59f57 Wave 2 of profile adjustments 2026-10-18 18:46:15 +02:00
Kenji_64 40e5ef1c46 Added Fearow placeholders and fixed its neck (also adjusted Swampert placeholder) 2026-10-18 18:45:31 +02:00
Kenji_64 5025977d23 Added transparency to Vikavolt 2026-10-18 18:44:22 +02:00
Kenji_64 922006afe7 Implemented Magby and Magmar animations 2026-10-18 16:14:45 +02:00
Kenji_64 46cc7cb04d Added placeholder animations to Mudkip, Marshtomp, Swampert and Spearow 2026-10-18 15:48:35 +02:00
Kenji_64 fa3e3bbb1b Scaled down a huge lot of small Pokémon's profiles and fixed some that were off over all 2026-10-18 15:47:46 +02:00
Kenji_64 afbdcce337 Added Eerie Spell activate and Hospitality lang (Hospitality's text does not work properly atm due to a Doubles bug) 2026-10-18 15:46:19 +02:00
Nico Merenq 1fa113f5ac Translated using Weblate (Russian)
Currently translated at 84.8% (8137 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ru/
2026-10-18 11:56:30 +00:00
whatsy f680682f61 Revert "Rescrape drops"
This reverts commit 6a7d5d2133.
2026-10-17 22:51:02 -04:00
whatsy 4d93d1244d Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-10-17 22:40:25 -04:00
whatsy 6a7d5d2133 Rescrape drops 2026-10-17 22:40:13 -04:00
ewwww 98397cd1da removed igglybuff, jigglypuff, and wigglytuff's rights to float 2026-10-17 21:57:59 -04:00
ewwww fc5a610394 added water shuriken fx 2026-10-17 20:37:00 -04:00
ewwww 504df5d998 sped up cacnea and hippowdon's walk speed 2026-10-17 20:16:26 -04:00
ewwww 3b4d3ebf50 fixed t-posing archeops during air_cry 2026-10-17 20:01:56 -04:00
Watuneni iteite 8443b88c16 Translated using Weblate (Japanese)
Currently translated at 99.0% (9500 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-16 17:12:33 +00:00
Gustavo Puerari b71208299f Translated using Weblate (Portuguese (Brazil))
Currently translated at 96.7% (9276 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/pt_BR/
2026-10-15 18:07:01 +00:00
Watuneni iteite 136b5824b0 Translated using Weblate (Japanese)
Currently translated at 98.7% (9464 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-15 18:06:58 +00:00
Watuneni iteite f9189b265f Translated using Weblate (Japanese)
Currently translated at 98.7% (9464 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-14 15:27:44 +00:00
dhi_holo 8e1fcc6f14 Structure Spawn Tags
Feel free to rename, delete, or rework any of these, it's moreso just setting the groundwork

`spawns/is_aquatic_monument` - Ocean Monuments and related structures
`spawns/is_aquatic_ruins` - Ocean Ruins and similar structures
`spawns/is_civilization` - Village and Towns tag, for anything that should spawn at any kind of village/town
`spawns/is_desert_monument` - Desert Pyramids and Temples
`spawns/is_dungeon` - Modded tag, the idea is things of the Stronghold *vibe*, but not being literal strongholds
`spawns/is_end_structure` - End Cities and related structures
`spawns/is_graveyard` - Modded tag, graveyards and crypts
`spawns/is_igloo` - just the vanilla igloo, it's me just wanting stuff in tags where it isn't yet so if some addon wants to give igloos another place they can
`spawns/is_illager_outpost` - small settlements for illagers, such as campsites and other small watchtower-like things
`spawns/is_illager_structure` - Bigger illager structures, the mansion, manors, stuff that has a sense of "infrastructure" I suppose
`spawns/is_jungle_monument` - Jungle Temples and Pyramids
`spawns/is_nether_structure` - is a structure of the Nether
`spawns/is_piglin_structure` - is a structure specifically inhabited by Piglins. Can be used to either add stuff specifically around Piglins, or to prevent things around them
`spawns/is_ruins` - Taking what we have as ruins and just applying that logic to other mods
`spawns/is_shipwreck` - not sure if we use this for spawns, but if we do this can be hooked into, if not then this could be deleted, no hassle, just felt like a logical thing to include. Is related to "surface" shipwrecks, not things like the coves
`spawns/is_witch_structure` - witch structures, like huts, villages, etc. Also added any structures that force black cat spawning, as that's used to reference to witches in the vanilla tags

Mods used within the tags so far:
https://modrinth.com/mod/dungeons-and-taverns (`nova_structures`)
A lot of https://modrinth.com/organization/nuVP8uUG mods (the Yung structure ones, usually starts as `better` and then the structure name)
https://modrinth.com/mod/enderscape
https://modrinth.com/mod/aquamirae
https://modrinth.com/mod/terralith
Will continue to investigate mods to add to these
2026-10-14 17:54:34 +10:00
Watuneni iteite 21c4d0233d Translated using Weblate (Japanese)
Currently translated at 98.7% (9464 of 9587 strings)

Translation: Cobblemon/Mod
Translate-URL: https://lang.cobblemon.com/projects/cobblemon/mod/ja/
2026-10-13 12:09:59 +00:00
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
662 changed files with 27269 additions and 4261 deletions

No files matched your search

+4 -77
View File
@@ -1,88 +1,15 @@
# Changelog
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
## [1.8.x (MONTH xth, 2026)](#1-8-x)
### Additions
### Cries added/updated for the following Pokémon
- Hoppip
- Skiploom
- Jumpluff
- Houndour
- Houndoom
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Surskit
- Masquerain
- Torkoal
- Starly
- Staravia
- Staraptor
- Combee
- Vespiquen
- Dusknoir
- Helioptile
- Heliolisk
- Grubbin
- Charjabug
- Vikavolt
- Applin (updated)
- Flapple (updated)
- Appletun
- Runerigus
- Dipplin
### Model updates for the following Pokémon
### Animation updates for the following Pokémon
- Vikavolt
### Changes
- Changed the Misc icon in the Campfire Pot Recipe Book to Health Candy
- Moved EXP Candies from the Medicine recipe book category to the Misc category
- Removed Dynamic Lights data from the Magmarizer item for consistency with the block
- Added `doPokemonPredation` gamerule, which controls aggressiveness of Pokémon with `hate_entity` brain task defined
- Added `predation_chance` server config value, which controls the possibility of attacking the hated entity found by `hate_entity` brain task
- Prehistoric Eroded Pillar generates slightly more frequently
### Fixes
- 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 the Pokédex evolution tree crashing when looking at a dex that is not part of the "National Dex" aggregate.
- Fixed evolution trees not building properly when a Pokémon has several Pokédex entries for different regions.
- 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.
- Fixed the `alpha` property having no tab completion in commands such as `/pokespawn`, `/pokegive` and `/pokeedit`, and added the missing `dmax_level`, `gmax_factor` and `tradable` aliases to tab completion
- Fixed friendship booster items not being recognised as such
- Fixed conflicts with other mods that would make use of recipe books in certain ways (Farmer's Delight Cooking Pot)
- Fixed Pokémon battles stalling when a sidemod cancels the `THROWN_POKEBALL_HIT` hit event
- Fixed choice feature tab completion in `spawnpokemon/pokegive/pokeedit` commands
- Fixed MoLang that's targeting players intermittedly failing when they have died, needing them to relog
- Fixed a NeoForge bug where none of the Cobblemon plants/berries could be placed into pots
- Fixed party overlay not hiding when the player opens the Pokédex scanner
- Added compat for AppleSkin
- Fixed some display names having unintended font colours
- Fixed `hate_entity` task focusing on its own entity
- Thus enabled deadly warfare between Zangoose and Seviper
- Prehistoric Frozen Pond, Prehistoric Frozen Spike, and Prehistoric Lush Den now generate
- Fixed Frogadier floating in its battle_idle animation
- Fixed absence or incorrect mergers in spawning conditions, making max height/depth of conditions to use the min value instead
- Added stricter riding checks on server side to prevent possible packet forgery when player tries to ride pokemon
- Fixed Pokémon cries and animation sounds played from GUIs, such as the starter selection screen and the Pokédex, being tied to the Master volume slider instead of Friendly Creatures
### Developer
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation
- New Platform Event `PLAYER_RESPAWN` which is fired when a player respawns (either through death or returning from the end)
- Platform Events `SERVER_PLAYER_TICK_PRE` and `SERVER_PLAYER_TICK_POST` no longer fire when the player is dead
- it is now tied much closer to the actual entity rather than the player (or their connection)
### Molang & Datapacks
- Fixed `q.pokemon.nature` returning class instance instead of proper string value (`cobblemon:adamant`)
- Fixed `q.entity.is_looking_at` result being always `1` for whatever entity passed as target
- Added hate chance optional parameter to `hate_entity` task. `1.0` will make an entity always attack the target
### Particles
### Localization
- Updated translations for:
- Updated translations for:
+107
View File
@@ -0,0 +1,107 @@
# Changelog
## [1.8.3 (October 25th, 2026)](#1-8-3)
### Additions
- Added compatability with AppleSkin.
### New Rideable Pokémon
- Vikavolt
- Fearow
### Particles
## New Move Animations
- Water Shuriken
### Cries added/updated for the following Pokémon
- Hoppip
- Skiploom
- Jumpluff
- Houndour
- Houndoom
- Wurmple
- Silcoon
- Beautifly
- Cascoon
- Dustox
- Surskit
- Masquerain
- Torkoal
- Starly
- Staravia
- Staraptor
- Combee
- Vespiquen
- Dusknoir
- Helioptile
- Heliolisk
- Grubbin
- Charjabug
- Vikavolt
- Applin (updated)
- Flapple (updated)
- Appletun
- Runerigus
- Dipplin
### Animation updates for the following Pokémon
- Vikavolt
- Magby
- Magmar
- Ninetales (+ Alolan)
- Spearow
- Fearow
- Squawkabilly
- Mudkip
- Marshtomp
- Swampert
### Changes
- Changed the Misc icon in the Campfire Pot Recipe Book to Health Candy.
- Moved EXP Candies from the Medicine recipe book category to the Misc category.
- Removed Dynamic Lights data from the Magmarizer item for consistency with the block.
- Added `doPokemonPredation` game rule, which controls aggressiveness of Pokémon with the `hate_entity` brain task defined.
- Added `predation_chance` server config value, which controls the possibility of attacking the hated entity found in `hate_entity` brain tasks.
- Prehistoric Eroded Pillar generates slightly more frequently.
- Togekiss was changed from a jet-style mount to bird-style.
### Fixes
- 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 the Pokédex evolution tree crashing when looking at a dex that is not part of the "National Dex" aggregate.
- Fixed some display names in the Pokédex evolution tree having unintended font colours.
- Fixed evolution trees not building properly when a Pokémon has several Pokédex entries for different regions.
- 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.
- Fixed the `alpha` property having no tab completion in commands such as `/pokespawn`, `/pokegive` and `/pokeedit`, and added the missing `dmax_level`, `gmax_factor` and `tradable` aliases to tab completion.
- Fixed friendship booster items not being recognised as such.
- Fixed conflicts with other mods that would make use of recipe books in certain ways (Farmer's Delight Cooking Pot).
- Fixed Pokémon battles stalling when a sidemod cancels the `THROWN_POKEBALL_HIT` hit event.
- Fixed choice feature tab completion in `spawnpokemon/pokegive/pokeedit` commands.
- Fixed MoLang that's targeting players intermittedly failing when they have died, requiring them to relog.
- Fixed a NeoForge bug where none of the Cobblemon plants/berries could be placed into pots.
- Fixed party overlay not hiding when the player opens the Pokédex scanner.
- Fixed `hate_entity` task focusing on its own entity, therefore enabling deadly warfare between Zangoose and Seviper.
- Prehistoric Frozen Pond, Prehistoric Frozen Spike, and Prehistoric Lush Den now actually generate (oops).
- Fixed Frogadier floating in its `battle_idle` animation.
- Fixed absence or incorrect mergers in spawning conditions, causing the max height/depth of conditions to use the min value instead.
- Added stricter riding checks on server side to prevent possible packet forgery when a player tries to ride Pokémon.
- Fixed Pokémon cries and animation sounds played from GUIs, such as the starter selection screen and the Pokédex, being tied to the Master volume slider instead of Friendly Creatures.
- Fixed Archeops T-posing during its `air_cry` animation.
### Developer
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation.
- New Platform Event `PLAYER_RESPAWN` which is fired when a player respawns (either through death or returning from the end).
- Platform Events `SERVER_PLAYER_TICK_PRE` and `SERVER_PLAYER_TICK_POST` no longer fire when the player is dead.
- It is now tied much closer to the actual entity rather than the player or their connection.
### Molang & Datapacks
- Fixed `q.pokemon.nature` returning class instance instead of proper string value (`cobblemon:adamant`).
- Fixed `q.entity.is_looking_at` result being always `1` for whatever entity passed as the target parameter.
- Added hate chance optional parameter to `hate_entity` task. `1.0` will make an entity always attack the target.
### Localization
- Updated translations for:
- Russian
- Japanese
- Brazilian Portuguese
+25
View File
@@ -0,0 +1,25 @@
# Changelog
## [1.8.4 (October 27th, 2026)](#1-8-4)
### Changes
- The Froakie line now blinks. The world will never be the same.
### Fixes
- Fixed heaps of spawns not working. This was caused by us missing something before release. RIP.
- Fixed a crash caused by Fearow blinking while you're riding one. Makes sense to me.
- Fixed Minior's "Shields Down" ability being very broken.
- Fixed Dusclops using Squirtle's cry sound (lol).
- Fixed blank Cooking Pot recipe book entries and missing tabs on Fabric.
- Fixed a hitbox issue with placeable Orb held items attached to a ceiling.
- Fixed something sneaky. I'll tell you about it later.
### Molang & Datapacks
- Added `q.target_fainted` function to move action effects that checks if any opponents fainted from the move.
### Particles
- Updated Water Shuriken effect to be timed better when the opponent faints.
### Localization
- Updated translations for:
- Korean
- Simplified Chinese
@@ -69,7 +69,7 @@ public abstract class ClientRecipeBookMixin {
value = "INVOKE",
target = "Lcom/google/common/collect/ImmutableMap;copyOf(Ljava/util/Map;)Lcom/google/common/collect/ImmutableMap;"
))
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci, @Local(name = "map2") Map<RecipeBookCategories, List<RecipeCollection>> map2) {
private void addCustomAggregateCategories(Iterable<RecipeHolder<?>> recipes, RegistryAccess registryAccess, CallbackInfo ci, @Local(ordinal = 1) Map<RecipeBookCategories, List<RecipeCollection>> map2) {
CobblemonRecipeCategories.Companion.getCustomAggregateCategories().forEach((recipeBookCategories, list) -> map2.put(recipeBookCategories, (List) list.stream().flatMap((recipeBookCategoriesx) -> ((List) map2.getOrDefault(recipeBookCategoriesx, ImmutableList.of())).stream()).collect(ImmutableList.toImmutableList())));
}
}
@@ -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,12 +59,32 @@ 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 || renderMode == ItemDisplayContext.NONE) {
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();
else if (stack.getItem() == CobblemonItems.TECHNICAL_MACHINE) {
if (stack.getItem() instanceof PokedexItem pokedexItem) resourceLocation = pokedexItem.getType().getItemSpritePath();
if (stack.getItem() == CobblemonItems.TECHNICAL_MACHINE) {
TMMoveComponent component = stack.get(CobblemonItemComponents.TM_MOVE);
if (component != null) {
resourceLocation = new ResourceLocation(Cobblemon.MODID, "tm/" + component.getMove().getElementalType().showdownId() + "_tm");
@@ -74,6 +96,9 @@ public abstract class ItemRendererMixin {
if (resourceLocation != null) {
BakedModel replacementModel = this.itemModelShaper.getModelManager().getModel(new ModelResourceLocation(resourceLocation, "inventory"));
if (!cobblemon$isSameModel(model, replacementModel)) {
if (stack.getItem() instanceof PokeBallItem) {
ci.cancel();
}
ci.cancel();
render(stack, renderMode, leftHanded, matrices, multiBufferSource, light, overlay, replacementModel);
}
@@ -83,16 +108,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);
@@ -103,10 +133,11 @@ public abstract class ItemRendererMixin {
// this falls back to comparing the model contents (e.g. location of the texture, width and height)
@Unique
private static boolean cobblemon$isSameModel(BakedModel original, BakedModel replacement) {
return original.equals(replacement) ||
(original.getParticleIcon().contents().name().equals(replacement.getParticleIcon().contents().name()) &&
original.getParticleIcon().contents().width() == replacement.getParticleIcon().contents().width() &&
original.getParticleIcon().contents().height() == replacement.getParticleIcon().contents().height());
return original.equals(replacement) || (
original.getParticleIcon().contents().name().equals(replacement.getParticleIcon().contents().name())
&& original.getParticleIcon().contents().width() == replacement.getParticleIcon().contents().width()
&& original.getParticleIcon().contents().height() == replacement.getParticleIcon().contents().height()
);
}
}
@@ -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,15 +41,18 @@ 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>",
at = @At(
value = "INVOKE",
target = "Lnet/minecraft/util/profiling/ProfilerFiller;popPush(Ljava/lang/String;)V",
ordinal = 1
ordinal = 1,
shift = At.Shift.AFTER
)
)
public void loadCustomItemModels(BlockColors blockColors,
@@ -67,13 +73,24 @@ 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 -> {
Map<ResourceLocation, Resource> map = Minecraft.getInstance().getResourceManager().listResources("models/item/" + folder, (t) -> true);
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);
});
}
});
});
}
@@ -20,14 +20,14 @@ import org.spongepowered.asm.mixin.injection.At;
@Mixin(RecipeBookComponent.class)
public class RecipeBookComponentMixin {
@Definition(id = "recipeBookCategories", local = @Local(name = "recipeBookCategories"))
@Definition(id = "recipeBookCategories", local = @Local(type = RecipeBookCategories.class))
@Definition(id = "FURNACE_SEARCH", field = "Lnet/minecraft/client/RecipeBookCategories;FURNACE_SEARCH:Lnet/minecraft/client/RecipeBookCategories;")
@Expression("recipeBookCategories != FURNACE_SEARCH")
@ModifyExpressionValue(
method = "updateTabs",
at = @At(value = "MIXINEXTRAS:EXPRESSION")
)
private boolean notCookingPotSearch(boolean original, @Local(name = "recipeBookCategories") RecipeBookCategories recipeBookCategories) {
private boolean notCookingPotSearch(boolean original, @Local RecipeBookCategories recipeBookCategories) {
return original && recipeBookCategories != CobblemonRecipeCategories.COOKING_POT_SEARCH.toVanillaCategory();
}
}
@@ -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
}
}
@@ -75,103 +75,105 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
val NPC_EDITOR = create("npc_editor", CobblemonItem(Item.Properties().stacksTo(1)))
@JvmField
val pokeBalls = mutableListOf<PokeBallItem>()
val pokeBalls = mutableMapOf< ResourceLocation, PokeBallItem>()
@JvmField
val POKE_BALL = pokeBallItem(PokeBalls.POKE_BALL)
val STRANGE_BALL = pokeBallItem("strange_ball")
@JvmField
val CITRINE_BALL = pokeBallItem(PokeBalls.CITRINE_BALL)
val POKE_BALL = pokeBallItem("poke_ball")
@JvmField
val VERDANT_BALL = pokeBallItem(PokeBalls.VERDANT_BALL)
val CITRINE_BALL = pokeBallItem("citrine_ball")
@JvmField
val AZURE_BALL = pokeBallItem(PokeBalls.AZURE_BALL)
val VERDANT_BALL = pokeBallItem("verdant_ball")
@JvmField
val ROSEATE_BALL = pokeBallItem(PokeBalls.ROSEATE_BALL)
val AZURE_BALL = pokeBallItem("azure_ball")
@JvmField
val SLATE_BALL = pokeBallItem(PokeBalls.SLATE_BALL)
val ROSEATE_BALL = pokeBallItem("roseate_ball")
@JvmField
val PREMIER_BALL = pokeBallItem(PokeBalls.PREMIER_BALL)
val SLATE_BALL = pokeBallItem("slate_ball")
@JvmField
val GREAT_BALL = pokeBallItem(PokeBalls.GREAT_BALL)
val PREMIER_BALL = pokeBallItem("premier_ball")
@JvmField
val ULTRA_BALL = pokeBallItem(PokeBalls.ULTRA_BALL)
val GREAT_BALL = pokeBallItem("great_ball")
@JvmField
val SAFARI_BALL = pokeBallItem(PokeBalls.SAFARI_BALL)
val ULTRA_BALL = pokeBallItem("ultra_ball")
@JvmField
val FAST_BALL = pokeBallItem(PokeBalls.FAST_BALL)
val SAFARI_BALL = pokeBallItem("safari_ball")
@JvmField
val LEVEL_BALL = pokeBallItem(PokeBalls.LEVEL_BALL)
val FAST_BALL = pokeBallItem("fast_ball")
@JvmField
val LURE_BALL = pokeBallItem(PokeBalls.LURE_BALL)
val LEVEL_BALL = pokeBallItem("level_ball")
@JvmField
val HEAVY_BALL = pokeBallItem(PokeBalls.HEAVY_BALL)
val LURE_BALL = pokeBallItem("lure_ball")
@JvmField
val LOVE_BALL = pokeBallItem(PokeBalls.LOVE_BALL)
val HEAVY_BALL = pokeBallItem("heavy_ball")
@JvmField
val FRIEND_BALL = pokeBallItem(PokeBalls.FRIEND_BALL)
val LOVE_BALL = pokeBallItem("love_ball")
@JvmField
val MOON_BALL = pokeBallItem(PokeBalls.MOON_BALL)
val FRIEND_BALL = pokeBallItem("friend_ball")
@JvmField
val SPORT_BALL = pokeBallItem(PokeBalls.SPORT_BALL)
val MOON_BALL = pokeBallItem("moon_ball")
@JvmField
val PARK_BALL = pokeBallItem(PokeBalls.PARK_BALL)
val SPORT_BALL = pokeBallItem("sport_ball")
@JvmField
val NET_BALL = pokeBallItem(PokeBalls.NET_BALL)
val PARK_BALL = pokeBallItem("park_ball")
@JvmField
val DIVE_BALL = pokeBallItem(PokeBalls.DIVE_BALL)
val NET_BALL = pokeBallItem("net_ball")
@JvmField
val NEST_BALL = pokeBallItem(PokeBalls.NEST_BALL)
val DIVE_BALL = pokeBallItem("dive_ball")
@JvmField
val REPEAT_BALL = pokeBallItem(PokeBalls.REPEAT_BALL)
val NEST_BALL = pokeBallItem("nest_ball")
@JvmField
val TIMER_BALL = pokeBallItem(PokeBalls.TIMER_BALL)
val REPEAT_BALL = pokeBallItem("repeat_ball")
@JvmField
val LUXURY_BALL = pokeBallItem(PokeBalls.LUXURY_BALL)
val TIMER_BALL = pokeBallItem("timer_ball")
@JvmField
val DUSK_BALL = pokeBallItem(PokeBalls.DUSK_BALL)
val LUXURY_BALL = pokeBallItem("luxury_ball")
@JvmField
val HEAL_BALL = pokeBallItem(PokeBalls.HEAL_BALL)
val DUSK_BALL = pokeBallItem("dusk_ball")
@JvmField
val QUICK_BALL = pokeBallItem(PokeBalls.QUICK_BALL)
val HEAL_BALL = pokeBallItem("heal_ball")
@JvmField
val DREAM_BALL = pokeBallItem(PokeBalls.DREAM_BALL)
val QUICK_BALL = pokeBallItem("quick_ball")
@JvmField
val BEAST_BALL = pokeBallItem(PokeBalls.BEAST_BALL)
val DREAM_BALL = pokeBallItem("dream_ball")
@JvmField
val MASTER_BALL = pokeBallItem(PokeBalls.MASTER_BALL)
val BEAST_BALL = pokeBallItem("beast_ball")
@JvmField
val CHERISH_BALL = pokeBallItem(PokeBalls.CHERISH_BALL)
val MASTER_BALL = pokeBallItem("master_ball")
@JvmField
val ANCIENT_POKE_BALL = pokeBallItem(PokeBalls.ANCIENT_POKE_BALL)
val CHERISH_BALL = pokeBallItem("cherish_ball")
@JvmField
val ANCIENT_CITRINE_BALL = pokeBallItem(PokeBalls.ANCIENT_CITRINE_BALL)
val ANCIENT_POKE_BALL = pokeBallItem("ancient_poke_ball")
@JvmField
val ANCIENT_VERDANT_BALL = pokeBallItem(PokeBalls.ANCIENT_VERDANT_BALL)
val ANCIENT_CITRINE_BALL = pokeBallItem("ancient_citrine_ball")
@JvmField
val ANCIENT_AZURE_BALL = pokeBallItem(PokeBalls.ANCIENT_AZURE_BALL)
val ANCIENT_VERDANT_BALL = pokeBallItem("ancient_verdant_ball")
@JvmField
val ANCIENT_ROSEATE_BALL = pokeBallItem(PokeBalls.ANCIENT_ROSEATE_BALL)
val ANCIENT_AZURE_BALL = pokeBallItem("ancient_azure_ball")
@JvmField
val ANCIENT_SLATE_BALL = pokeBallItem(PokeBalls.ANCIENT_SLATE_BALL)
val ANCIENT_ROSEATE_BALL = pokeBallItem("ancient_roseate_ball")
@JvmField
val ANCIENT_IVORY_BALL = pokeBallItem(PokeBalls.ANCIENT_IVORY_BALL)
val ANCIENT_SLATE_BALL = pokeBallItem("ancient_slate_ball")
@JvmField
val ANCIENT_GREAT_BALL = pokeBallItem(PokeBalls.ANCIENT_GREAT_BALL)
val ANCIENT_IVORY_BALL = pokeBallItem("ancient_ivory_ball")
@JvmField
val ANCIENT_ULTRA_BALL = pokeBallItem(PokeBalls.ANCIENT_ULTRA_BALL)
val ANCIENT_GREAT_BALL = pokeBallItem("ancient_great_ball")
@JvmField
val ANCIENT_FEATHER_BALL = pokeBallItem(PokeBalls.ANCIENT_FEATHER_BALL)
val ANCIENT_ULTRA_BALL = pokeBallItem("ancient_ultra_ball")
@JvmField
val ANCIENT_WING_BALL = pokeBallItem(PokeBalls.ANCIENT_WING_BALL)
val ANCIENT_FEATHER_BALL = pokeBallItem("ancient_feather_ball")
@JvmField
val ANCIENT_JET_BALL = pokeBallItem(PokeBalls.ANCIENT_JET_BALL)
val ANCIENT_WING_BALL = pokeBallItem("ancient_wing_ball")
@JvmField
val ANCIENT_HEAVY_BALL = pokeBallItem(PokeBalls.ANCIENT_HEAVY_BALL)
val ANCIENT_JET_BALL = pokeBallItem("ancient_jet_ball")
@JvmField
val ANCIENT_LEADEN_BALL = pokeBallItem(PokeBalls.ANCIENT_LEADEN_BALL)
val ANCIENT_HEAVY_BALL = pokeBallItem("ancient_heavy_ball")
@JvmField
val ANCIENT_GIGATON_BALL = pokeBallItem(PokeBalls.ANCIENT_GIGATON_BALL)
val ANCIENT_LEADEN_BALL = pokeBallItem("ancient_leaden_ball")
@JvmField
val ANCIENT_ORIGIN_BALL = pokeBallItem(PokeBalls.ANCIENT_ORIGIN_BALL)
val ANCIENT_GIGATON_BALL = pokeBallItem("ancient_gigaton_ball")
@JvmField
val ANCIENT_ORIGIN_BALL = pokeBallItem("ancient_origin_ball")
val pokedexes = mutableListOf<PokedexItem>()
@JvmField
@@ -1406,102 +1408,102 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
)
val pokeRods = mutableListOf<PokerodItem>()
@JvmField
val POKE_ROD = pokerodItem(cobblemonResource("poke_rod"))
@JvmField
val CITRINE_ROD = pokerodItem(cobblemonResource("citrine_rod"))
@JvmField
val VERDANT_ROD = pokerodItem(cobblemonResource("verdant_rod"))
@JvmField
val AZURE_ROD = pokerodItem(cobblemonResource("azure_rod"))
@JvmField
val ROSEATE_ROD = pokerodItem(cobblemonResource("roseate_rod"))
@JvmField
val SLATE_ROD = pokerodItem(cobblemonResource("slate_rod"))
@JvmField
val PREMIER_ROD = pokerodItem(cobblemonResource("premier_rod"))
@JvmField
val GREAT_ROD = pokerodItem(cobblemonResource("great_rod"))
@JvmField
val ULTRA_ROD = pokerodItem(cobblemonResource("ultra_rod"))
@JvmField
val SAFARI_ROD = pokerodItem(cobblemonResource("safari_rod"))
@JvmField
val FAST_ROD = pokerodItem(cobblemonResource("fast_rod"))
@JvmField
val LEVEL_ROD = pokerodItem(cobblemonResource("level_rod"))
@JvmField
val LURE_ROD = pokerodItem(cobblemonResource("lure_rod"))
@JvmField
val HEAVY_ROD = pokerodItem(cobblemonResource("heavy_rod"))
@JvmField
val LOVE_ROD = pokerodItem(cobblemonResource("love_rod"))
@JvmField
val FRIEND_ROD = pokerodItem(cobblemonResource("friend_rod"))
@JvmField
val MOON_ROD = pokerodItem(cobblemonResource("moon_rod"))
@JvmField
val SPORT_ROD = pokerodItem(cobblemonResource("sport_rod"))
@JvmField
val PARK_ROD = pokerodItem(cobblemonResource("park_rod"))
@JvmField
val NET_ROD = pokerodItem(cobblemonResource("net_rod"))
@JvmField
val DIVE_ROD = pokerodItem(cobblemonResource("dive_rod"))
@JvmField
val NEST_ROD = pokerodItem(cobblemonResource("nest_rod"))
@JvmField
val REPEAT_ROD = pokerodItem(cobblemonResource("repeat_rod"))
@JvmField
val TIMER_ROD = pokerodItem(cobblemonResource("timer_rod"))
@JvmField
val LUXURY_ROD = pokerodItem(cobblemonResource("luxury_rod"))
@JvmField
val DUSK_ROD = pokerodItem(cobblemonResource("dusk_rod"))
@JvmField
val HEAL_ROD = pokerodItem(cobblemonResource("heal_rod"))
@JvmField
val QUICK_ROD = pokerodItem(cobblemonResource("quick_rod"))
@JvmField
val DREAM_ROD = pokerodItem(cobblemonResource("dream_rod"))
@JvmField
val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"), Rarity.RARE)
@JvmField
val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"), Rarity.EPIC)
@JvmField
val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"), Rarity.EPIC)
@JvmField
val ANCIENT_POKE_ROD = pokerodItem(cobblemonResource("ancient_poke_rod"))
@JvmField
val ANCIENT_CITRINE_ROD = pokerodItem(cobblemonResource("ancient_citrine_rod"))
@JvmField
val ANCIENT_VERDANT_ROD = pokerodItem(cobblemonResource("ancient_verdant_rod"))
@JvmField
val ANCIENT_AZURE_ROD = pokerodItem(cobblemonResource("ancient_azure_rod"))
@JvmField
val ANCIENT_ROSEATE_ROD = pokerodItem(cobblemonResource("ancient_roseate_rod"))
@JvmField
val ANCIENT_SLATE_ROD = pokerodItem(cobblemonResource("ancient_slate_rod"))
@JvmField
val ANCIENT_IVORY_ROD = pokerodItem(cobblemonResource("ancient_ivory_rod"))
@JvmField
val ANCIENT_GREAT_ROD = pokerodItem(cobblemonResource("ancient_great_rod"))
@JvmField
val ANCIENT_ULTRA_ROD = pokerodItem(cobblemonResource("ancient_ultra_rod"))
@JvmField
val ANCIENT_FEATHER_ROD = pokerodItem(cobblemonResource("ancient_feather_rod"))
@JvmField
val ANCIENT_WING_ROD = pokerodItem(cobblemonResource("ancient_wing_rod"))
@JvmField
val ANCIENT_JET_ROD = pokerodItem(cobblemonResource("ancient_jet_rod"))
@JvmField
val ANCIENT_HEAVY_ROD = pokerodItem(cobblemonResource("ancient_heavy_rod"))
@JvmField
val ANCIENT_LEADEN_ROD = pokerodItem(cobblemonResource("ancient_leaden_rod"))
@JvmField
val ANCIENT_GIGATON_ROD = pokerodItem(cobblemonResource("ancient_gigaton_rod"))
@JvmField
val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"), Rarity.EPIC)
// @JvmField
// val POKE_ROD = pokerodItem(cobblemonResource("poke_rod"))
// @JvmField
// val CITRINE_ROD = pokerodItem(cobblemonResource("citrine_rod"))
// @JvmField
// val VERDANT_ROD = pokerodItem(cobblemonResource("verdant_rod"))
// @JvmField
// val AZURE_ROD = pokerodItem(cobblemonResource("azure_rod"))
// @JvmField
// val ROSEATE_ROD = pokerodItem(cobblemonResource("roseate_rod"))
// @JvmField
// val SLATE_ROD = pokerodItem(cobblemonResource("slate_rod"))
// @JvmField
// val PREMIER_ROD = pokerodItem(cobblemonResource("premier_rod"))
// @JvmField
// val GREAT_ROD = pokerodItem(cobblemonResource("great_rod"))
// @JvmField
// val ULTRA_ROD = pokerodItem(cobblemonResource("ultra_rod"))
// @JvmField
// val SAFARI_ROD = pokerodItem(cobblemonResource("safari_rod"))
// @JvmField
// val FAST_ROD = pokerodItem(cobblemonResource("fast_rod"))
// @JvmField
// val LEVEL_ROD = pokerodItem(cobblemonResource("level_rod"))
// @JvmField
// val LURE_ROD = pokerodItem(cobblemonResource("lure_rod"))
// @JvmField
// val HEAVY_ROD = pokerodItem(cobblemonResource("heavy_rod"))
// @JvmField
// val LOVE_ROD = pokerodItem(cobblemonResource("love_rod"))
// @JvmField
// val FRIEND_ROD = pokerodItem(cobblemonResource("friend_rod"))
// @JvmField
// val MOON_ROD = pokerodItem(cobblemonResource("moon_rod"))
// @JvmField
// val SPORT_ROD = pokerodItem(cobblemonResource("sport_rod"))
// @JvmField
// val PARK_ROD = pokerodItem(cobblemonResource("park_rod"))
// @JvmField
// val NET_ROD = pokerodItem(cobblemonResource("net_rod"))
// @JvmField
// val DIVE_ROD = pokerodItem(cobblemonResource("dive_rod"))
// @JvmField
// val NEST_ROD = pokerodItem(cobblemonResource("nest_rod"))
// @JvmField
// val REPEAT_ROD = pokerodItem(cobblemonResource("repeat_rod"))
// @JvmField
// val TIMER_ROD = pokerodItem(cobblemonResource("timer_rod"))
// @JvmField
// val LUXURY_ROD = pokerodItem(cobblemonResource("luxury_rod"))
// @JvmField
// val DUSK_ROD = pokerodItem(cobblemonResource("dusk_rod"))
// @JvmField
// val HEAL_ROD = pokerodItem(cobblemonResource("heal_rod"))
// @JvmField
// val QUICK_ROD = pokerodItem(cobblemonResource("quick_rod"))
// @JvmField
// val DREAM_ROD = pokerodItem(cobblemonResource("dream_rod"))
// @JvmField
// val BEAST_ROD = pokerodItem(cobblemonResource("beast_rod"), Rarity.RARE)
// @JvmField
// val MASTER_ROD = pokerodItem(cobblemonResource("master_rod"), Rarity.EPIC)
// @JvmField
// val CHERISH_ROD = pokerodItem(cobblemonResource("cherish_rod"), Rarity.EPIC)
// @JvmField
// val ANCIENT_POKE_ROD = pokerodItem(cobblemonResource("ancient_poke_rod"))
// @JvmField
// val ANCIENT_CITRINE_ROD = pokerodItem(cobblemonResource("ancient_citrine_rod"))
// @JvmField
// val ANCIENT_VERDANT_ROD = pokerodItem(cobblemonResource("ancient_verdant_rod"))
// @JvmField
// val ANCIENT_AZURE_ROD = pokerodItem(cobblemonResource("ancient_azure_rod"))
// @JvmField
// val ANCIENT_ROSEATE_ROD = pokerodItem(cobblemonResource("ancient_roseate_rod"))
// @JvmField
// val ANCIENT_SLATE_ROD = pokerodItem(cobblemonResource("ancient_slate_rod"))
// @JvmField
// val ANCIENT_IVORY_ROD = pokerodItem(cobblemonResource("ancient_ivory_rod"))
// @JvmField
// val ANCIENT_GREAT_ROD = pokerodItem(cobblemonResource("ancient_great_rod"))
// @JvmField
// val ANCIENT_ULTRA_ROD = pokerodItem(cobblemonResource("ancient_ultra_rod"))
// @JvmField
// val ANCIENT_FEATHER_ROD = pokerodItem(cobblemonResource("ancient_feather_rod"))
// @JvmField
// val ANCIENT_WING_ROD = pokerodItem(cobblemonResource("ancient_wing_rod"))
// @JvmField
// val ANCIENT_JET_ROD = pokerodItem(cobblemonResource("ancient_jet_rod"))
// @JvmField
// val ANCIENT_HEAVY_ROD = pokerodItem(cobblemonResource("ancient_heavy_rod"))
// @JvmField
// val ANCIENT_LEADEN_ROD = pokerodItem(cobblemonResource("ancient_leaden_rod"))
// @JvmField
// val ANCIENT_GIGATON_ROD = pokerodItem(cobblemonResource("ancient_gigaton_rod"))
// @JvmField
// val ANCIENT_ORIGIN_ROD = pokerodItem(cobblemonResource("ancient_origin_rod"), Rarity.EPIC)
// Misc
@JvmField
@@ -1584,15 +1586,19 @@ object CobblemonItems : PlatformRegistry<Registry<Item>, ResourceKey<Registry<It
private fun noSettingsItem(name: String): CobblemonItem = this.create(name, CobblemonItem(Item.Properties()))
private fun itemWithRarity(name: String, rarity: Rarity): CobblemonItem = this.create(name, CobblemonItem(Item.Properties().rarity(rarity)))
fun berries() = this.berries.toMap()
private fun mulchItem(name: String, mulchVariant: MulchVariant): MulchItem = this.create(name, MulchItem(mulchVariant))
private fun pokeBallItem(pokeBall: PokeBall): PokeBallItem {
val item = create(pokeBall.name.path, PokeBallItem(pokeBall))
pokeBall.item = item
pokeBalls.add(item)
private fun pokeBallItem(name: String): PokeBallItem {
// we attach strange ball by default to let registry load
// actual ball configurations will be attached on datapack loading stage
// see PokeBalls.reload
val ball = PokeBalls.STRANGE_BALL
val item = create(name, PokeBallItem(ball))
ball.stack = item.defaultInstance
pokeBalls.put(cobblemonResource(name), item)
return item
}
@@ -101,6 +101,7 @@ import com.cobblemon.mod.common.net.messages.client.pasture.OpenPasturePacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonPasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.PokemonUnpasturedPacket
import com.cobblemon.mod.common.net.messages.client.pasture.UpdatePastureConflictFlagPacket
import com.cobblemon.mod.common.net.messages.client.pokeballs.PokeBallsRegistrySyncPacket
import com.cobblemon.mod.common.net.messages.client.pokedex.ServerConfirmedRegisterPacket
import com.cobblemon.mod.common.net.messages.client.pokemon.update.*
import com.cobblemon.mod.common.net.messages.client.pokemon.update.evolution.AddEvolutionPacket
@@ -445,6 +446,7 @@ object CobblemonNetwork {
list.add(PacketRegisterInfo(MarkRegistrySyncPacket.ID, MarkRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(RideSettingsSyncPacket.ID, RideSettingsSyncPacket::decode, RideSettingsSyncHandler))
list.add(PacketRegisterInfo(TechnicalMachineRegistrySyncPacket.ID, TechnicalMachineRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
list.add(PacketRegisterInfo(PokeBallsRegistrySyncPacket.ID, PokeBallsRegistrySyncPacket::decode, DataRegistrySyncPacketHandler()))
// Effects
list.add(PacketRegisterInfo(SpawnSnowstormParticlePacket.ID, SpawnSnowstormParticlePacket::decode, SpawnSnowstormParticleHandler))
@@ -657,4 +659,4 @@ object CobblemonNetwork {
fun sendPacketToPlayer(player: ServerPlayer, packet: NetworkPacket<*>) {
Cobblemon.implementation.networkManager.sendPacketToPlayer(player, packet)
}
}
}
@@ -239,6 +239,10 @@ open class PokemonBattle(
}
fun writeShowdownAction(vararg messages: String) {
if (messages.any { it.contains("\n") || it.contains("\r") }) {
LOGGER.warn("Refusing to forward Showdown action with an embedded line break for battle {}: {}", battleId, messages.joinToString(" | "))
return
}
log(messages.joinToString("\n"))
ShowdownService.service.send(battleId, messages.toList().toTypedArray())
}
@@ -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,293 +8,348 @@
package com.cobblemon.mod.common.api.pokeball
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.CobblemonItems
import com.cobblemon.mod.common.api.Priority
import com.cobblemon.mod.common.api.data.JsonDataRegistry
import com.cobblemon.mod.common.api.data.ServerJsonDataRegistry
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.ExpressionModifier
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.net.messages.client.pokeballs.PokeBallsRegistrySyncPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.adapters.*
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.removeIf
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.core.HolderLookup
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
/**
* The data registry for [PokeBall]s.
* All the pokeball fields are guaranteed to exist
*/
object PokeBalls : JsonDataRegistry<PokeBall> {
override val id = cobblemonResource("pokeballs")
object PokeBalls : ServerJsonDataRegistry<PokeBall> {
// <editor-fold desc="Registry definitions">
override val type = PackType.SERVER_DATA
override val resourcePath = "pokeballs"
override val id = cobblemonResource(resourcePath)
override val observable = SimpleObservable<PokeBalls>()
override val typeToken: TypeToken<PokeBall> = TypeToken.get(PokeBall::class.java)
// ToDo once datapack pokeball is implemented add required adapters here
override val gson: Gson = GsonBuilder()
override fun setupParser(registry: HolderLookup.Provider): 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)
.registerTypeAdapter(PokeBall::class.java, PokeBallAdapter)
.registerTypeAdapterFactory(OptionalTypeAdapterFactory())
.registerTypeAdapterFactory(LowerCaseEnumTypeAdapterFactory())
.disableHtmlEscaping()
.setPrettyPrinting()
.create()
override val typeToken: TypeToken<PokeBall> = TypeToken.get(PokeBall::class.java)
override val resourcePath = "pokeballs"
private val defaults = hashMapOf<ResourceLocation, PokeBall>()
// ToDo datapack pokeball type here instead
private val custom = hashMapOf<ResourceLocation, PokeBall>()
private val entries = hashMapOf<ResourceLocation, PokeBall>()
// </editor-fold>
// <editor-fold desc="Default balls">
@JvmStatic
@get:JvmName("getPokeBall")
val POKE_BALL
get() = this.byName("poke_ball")
get() = getPokeBall("poke_ball")
@JvmStatic
@get:JvmName("getSlateBall")
val SLATE_BALL
get() = this.byName("slate_ball")
get() = getPokeBall("slate_ball")
@JvmStatic
@get:JvmName("getAzureBall")
val AZURE_BALL
get() = this.byName("azure_ball")
get() = getPokeBall("azure_ball")
@JvmStatic
@get:JvmName("getVerdantBall")
val VERDANT_BALL
get() = this.byName("verdant_ball")
get() = getPokeBall("verdant_ball")
@JvmStatic
@get:JvmName("getRoseateBall")
val ROSEATE_BALL
get() = this.byName("roseate_ball")
get() = getPokeBall("roseate_ball")
@JvmStatic
@get:JvmName("getCitrineBall")
val CITRINE_BALL
get() = this.byName("citrine_ball")
get() = getPokeBall("citrine_ball")
@JvmStatic
@get:JvmName("getGreatBall")
val GREAT_BALL
get() = this.byName("great_ball")
get() = getPokeBall("great_ball")
@JvmStatic
@get:JvmName("getUltraBall")
val ULTRA_BALL
get() = this.byName("ultra_ball")
get() = getPokeBall("ultra_ball")
@JvmStatic
@get:JvmName("getMasterBall")
val MASTER_BALL
get() = this.byName("master_ball")
get() = getPokeBall("master_ball")
@JvmStatic
@get:JvmName("getSafariBall")
val SAFARI_BALL
get() = this.byName("safari_ball")
get() = getPokeBall("safari_ball")
@JvmStatic
@get:JvmName("getFastBall")
val FAST_BALL
get() = this.byName("fast_ball")
get() = getPokeBall("fast_ball")
@JvmStatic
@get:JvmName("getLevelBall")
val LEVEL_BALL
get() = this.byName("level_ball")
get() = getPokeBall("level_ball")
@JvmStatic
@get:JvmName("getLureBall")
val LURE_BALL
get() = this.byName("lure_ball")
get() = getPokeBall("lure_ball")
@JvmStatic
@get:JvmName("getHeavyBall")
val HEAVY_BALL
get() = this.byName("heavy_ball")
get() = getPokeBall("heavy_ball")
@JvmStatic
@get:JvmName("getLoveBall")
val LOVE_BALL
get() = this.byName("love_ball")
get() = getPokeBall("love_ball")
@JvmStatic
@get:JvmName("getFriendBall")
val FRIEND_BALL
get() = this.byName("friend_ball")
get() = getPokeBall("friend_ball")
@JvmStatic
@get:JvmName("getMoonBall")
val MOON_BALL
get() = this.byName("moon_ball")
get() = getPokeBall("moon_ball")
@JvmStatic
@get:JvmName("getSportBall")
val SPORT_BALL
get() = this.byName("sport_ball")
get() = getPokeBall("sport_ball")
@JvmStatic
@get:JvmName("getNetBall")
val NET_BALL
get() = this.byName("net_ball")
get() = getPokeBall("net_ball")
@JvmStatic
@get:JvmName("getDiveBall")
val DIVE_BALL
get() = this.byName("dive_ball")
get() = getPokeBall("dive_ball")
@JvmStatic
@get:JvmName("getNestBall")
val NEST_BALL
get() = this.byName("nest_ball")
get() = getPokeBall("nest_ball")
@JvmStatic
@get:JvmName("getRepeatBall")
val REPEAT_BALL
get() = this.byName("repeat_ball")
get() = getPokeBall("repeat_ball")
@JvmStatic
@get:JvmName("getTimerBall")
val TIMER_BALL
get() = this.byName("timer_ball")
get() = getPokeBall("timer_ball")
@JvmStatic
@get:JvmName("getLuxuryBall")
val LUXURY_BALL
get() = this.byName("luxury_ball")
get() = getPokeBall("luxury_ball")
@JvmStatic
@get:JvmName("getPremierBall")
val PREMIER_BALL
get() = this.byName("premier_ball")
get() = getPokeBall("premier_ball")
@JvmStatic
@get:JvmName("getDuskBall")
val DUSK_BALL
get() = this.byName("dusk_ball")
get() = getPokeBall("dusk_ball")
@JvmStatic
@get:JvmName("getHealBall")
val HEAL_BALL
get() = this.byName("heal_ball")
get() = getPokeBall("heal_ball")
@JvmStatic
@get:JvmName("getQuickBall")
val QUICK_BALL
get() = this.byName("quick_ball")
get() = getPokeBall("quick_ball")
@JvmStatic
@get:JvmName("getCherishBall")
val CHERISH_BALL
get() = this.byName("cherish_ball")
get() = getPokeBall("cherish_ball")
@JvmStatic
@get:JvmName("getParkBall")
val PARK_BALL
get() = this.byName("park_ball")
get() = getPokeBall("park_ball")
@JvmStatic
@get:JvmName("getDreamBall")
val DREAM_BALL
get() = this.byName("dream_ball")
get() = getPokeBall("dream_ball")
@JvmStatic
@get:JvmName("getBeastBall")
val BEAST_BALL
get() = this.byName("beast_ball")
get() = getPokeBall("beast_ball")
@JvmStatic
@get:JvmName("getAncientPokeBall")
val ANCIENT_POKE_BALL
get() = this.byName("ancient_poke_ball")
get() = getPokeBall("ancient_poke_ball")
@JvmStatic
@get:JvmName("getAncientCitrineBall")
val ANCIENT_CITRINE_BALL
get() = this.byName("ancient_citrine_ball")
get() = getPokeBall("ancient_citrine_ball")
@JvmStatic
@get:JvmName("getAncientVerdantBall")
val ANCIENT_VERDANT_BALL
get() = this.byName("ancient_verdant_ball")
get() = getPokeBall("ancient_verdant_ball")
@JvmStatic
@get:JvmName("getAncientAzureBall")
val ANCIENT_AZURE_BALL
get() = this.byName("ancient_azure_ball")
get() = getPokeBall("ancient_azure_ball")
@JvmStatic
@get:JvmName("getAncientRoseateBall")
val ANCIENT_ROSEATE_BALL
get() = this.byName("ancient_roseate_ball")
get() = getPokeBall("ancient_roseate_ball")
@JvmStatic
@get:JvmName("getAncientSlateBall")
val ANCIENT_SLATE_BALL
get() = this.byName("ancient_slate_ball")
get() = getPokeBall("ancient_slate_ball")
@JvmStatic
@get:JvmName("getAncientIvoryBall")
val ANCIENT_IVORY_BALL
get() = this.byName("ancient_ivory_ball")
get() = getPokeBall("ancient_ivory_ball")
@JvmStatic
@get:JvmName("getAncientGreatBall")
val ANCIENT_GREAT_BALL
get() = this.byName("ancient_great_ball")
get() = getPokeBall("ancient_great_ball")
@JvmStatic
@get:JvmName("getAncientUltraBall")
val ANCIENT_ULTRA_BALL
get() = this.byName("ancient_ultra_ball")
get() = getPokeBall("ancient_ultra_ball")
@JvmStatic
@get:JvmName("getAncientHeavyBall")
val ANCIENT_HEAVY_BALL
get() = this.byName("ancient_heavy_ball")
get() = getPokeBall("ancient_heavy_ball")
@JvmStatic
@get:JvmName("getAncientLeadenBall")
val ANCIENT_LEADEN_BALL
get() = this.byName("ancient_leaden_ball")
get() = getPokeBall("ancient_leaden_ball")
@JvmStatic
@get:JvmName("getAncientGigatonBall")
val ANCIENT_GIGATON_BALL
get() = this.byName("ancient_gigaton_ball")
get() = getPokeBall("ancient_gigaton_ball")
@JvmStatic
@get:JvmName("getAncientFeatherBall")
val ANCIENT_FEATHER_BALL
get() = this.byName("ancient_feather_ball")
get() = getPokeBall("ancient_feather_ball")
@JvmStatic
@get:JvmName("getAncientWingBall")
val ANCIENT_WING_BALL
get() = this.byName("ancient_wing_ball")
get() = getPokeBall("ancient_wing_ball")
@JvmStatic
@get:JvmName("getAncientJetBall")
val ANCIENT_JET_BALL
get() = this.byName("ancient_jet_ball")
get() = getPokeBall("ancient_jet_ball")
@JvmStatic
@get:JvmName("getAncientOriginBall")
val ANCIENT_ORIGIN_BALL
get() = this.byName("ancient_origin_ball")
get() = getPokeBall("ancient_origin_ball")
@JvmStatic
@get:JvmName("getStrangeBall")
val STRANGE_BALL = create("strange_ball")
// </editor-fold>
init {
createDefault("poke_ball")
createDefault("slate_ball")
createDefault("azure_ball")
createDefault("verdant_ball")
createDefault("roseate_ball")
createDefault("citrine_ball")
createDefault("great_ball", MultiplierModifier(1.5F))
createDefault("ultra_ball", MultiplierModifier(2F))
createDefault("master_ball", GuaranteedModifier())
createDefault("safari_ball", CatchRateModifiers.SAFARI)
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 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("moon_ball", CatchRateModifiers.MOON_PHASES)
createDefault("sport_ball", MultiplierModifier(1.5F))
createDefault("net_ball", CatchRateModifiers.typeBoosting(3F, ElementalTypes.BUG, ElementalTypes.WATER))
createDefault("dive_ball", CatchRateModifiers.SUBMERGED_IN_WATER, waterDragValue = 0.99F)
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("premier_ball")
createDefault("dusk_ball", CatchRateModifiers.LIGHT_LEVEL)
createDefault("heal_ball", effects = listOf(CaptureEffects.FULL_RESTORE))
createDefault("quick_ball", CatchRateModifiers.turnBased { turn -> if (turn == 1) 5F else 1F })
createDefault("cherish_ball")
createDefault("park_ball", CatchRateModifiers.PARK)
createDefault("dream_ball", CatchRateModifiers.statusBoosting(4F, Statuses.SLEEP))
createDefault("beast_ball", DynamicMultiplierModifier({ _, pokemon -> if (pokemon.hasLabels(CobblemonPokemonLabels.ULTRA_BEAST)) 5F else 0.1F }) { _, _ -> true })
createDefault("ancient_poke_ball", ancient = true)
createDefault("ancient_citrine_ball", ancient = true)
createDefault("ancient_verdant_ball", ancient = true)
createDefault("ancient_azure_ball", ancient = true)
createDefault("ancient_roseate_ball", ancient = true)
createDefault("ancient_slate_ball", ancient = true)
createDefault("ancient_ivory_ball", ancient = true)
createDefault("ancient_great_ball", MultiplierModifier(1.5F), ancient = true)
createDefault("ancient_ultra_ball", MultiplierModifier(2F), ancient = true)
createDefault("ancient_heavy_ball", throwPower = 0.75f, ancient = true)
createDefault("ancient_leaden_ball", MultiplierModifier(1.5F), throwPower = 0.75f, ancient = true)
createDefault("ancient_gigaton_ball", MultiplierModifier(2F), throwPower = 0.75f, ancient = true)
createDefault("ancient_feather_ball", throwPower = 2.5f, ancient = true)
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)
// we need at least one default ball in case of shenanigans
create("strange_ball")
create("safari_ball", CatchRateModifiers.SAFARI)
create("fast_ball", BaseStatModifier(Stats.SPEED, 100, BaseStatModifier.Comparator.GTE, 4F))
create("level_ball", CatchRateModifiers.LEVEL)
create("lure_ball", CatchRateModifiers.LURE)
create("heavy_ball", CatchRateModifiers.WEIGHT_BASED)
create("love_ball", CatchRateModifiers.LOVE)
create("friend_ball", effects = listOf("q.pokemon.set_friendship(150.0)".asExpressionLike()))
create("moon_ball", CatchRateModifiers.MOON_PHASES)
create("net_ball", CatchRateModifiers.typeBoosting(3F, ElementalTypes.BUG, ElementalTypes.WATER))
create("dive_ball", CatchRateModifiers.SUBMERGED_IN_WATER, waterDragValue = 0.99F)
create("nest_ball", CatchRateModifiers.NEST)
create("repeat_ball", CatchRateModifiers.REPEAT)
create("timer_ball", CatchRateModifiers.turnBased { turn -> (1F + turn * (1229F / 4096F)).coerceAtMost(4F) })
create("luxury_ball" /*effects = listOf(FriendshipEarningBoostEffect(2F))*/)
create("dusk_ball", CatchRateModifiers.LIGHT_LEVEL)
create("heal_ball", effects = listOf("q.pokemon.heal()".asExpressionLike()))
create("quick_ball", CatchRateModifiers.turnBased { turn -> if (turn == 1) 5F else 1F })
create("park_ball", CatchRateModifiers.PARK)
create("dream_ball", CatchRateModifiers.statusBoosting(4F, Statuses.SLEEP))
create(
"beast_ball",
DynamicMultiplierModifier({ _, pokemon -> if (pokemon.hasLabels(CobblemonPokemonLabels.ULTRA_BEAST)) 5F else 0.1F }) { _, _ -> true })
// 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) {
@@ -310,235 +365,73 @@ 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
override fun reload(
data: Map<ResourceLocation, PokeBall>,
registry: HolderLookup.Provider
) {
this.entries.removeIf { it.key != cobblemonResource("strange_ball") }
data.entries.forEach {
val (key, ball) = it
ball.name = key
when(CobblemonItems.pokeBalls.containsKey(key)) {
true -> {
val item = CobblemonItems.pokeBalls.get(key)!!
item.pokeBall = ball
ball.stack = item.defaultInstance
}
false -> {
ball.stack = PokeBallComponent.createStack(ball)
}
}
this.entries[key] = ball
}
Cobblemon.LOGGER.info("Loaded ${data.size} poke balls.")
}
override fun sync(player: ServerPlayer) {
// ToDo once datapack pokeball is implemented sync them here
PokeBallsRegistrySyncPacket(this.entries).sendToPlayer(player)
}
/**
* 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.entries[name] ?: STRANGE_BALL
@JvmStatic
fun all() = this.defaults.filterKeys { !this.custom.containsKey(it) }.values + this.custom.values
fun getPokeBall(name: String): PokeBall = getPokeBall(cobblemonResource(name))
private fun createDefault(
@JvmStatic
fun all() = this.entries.values
fun create(
name: String,
modifier: CatchRateModifier = MultiplierModifier(1F) { _, _ -> true },
effects: List<CaptureEffect> = emptyList(),
modifier: CatchRateModifier = ExpressionModifier(),
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
): PokeBall {
val identifier = cobblemonResource(name)
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient)
this.defaults[identifier] = pokeball
return pokeball
ancient: Boolean = false,
color: Color = Color.RED
): PokeBall = PokeBall(
cobblemonResource(name),
modifier,
effects,
PokeBall.PokeBallProperties(),
waterDragValue,
model2d,
model3d,
throwPower,
ancient,
color
).also {
this.entries[it.name] = it
return it
}
private 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,76 @@
/*
* 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 {
if (query.getString().trim() == "1.0") return 1.0f
val biomeValue = thrower.level().getBiome(thrower.position().toBlockPos()).asBiomeMoLangValue()
val worldValue = thrower
.level()
.registryAccess()
.registryOrThrow(Registries.DIMENSION)
.wrapAsHolder(thrower.level())
.asWorldMoLangValue()
val ballRuntime = runtime
.withQueryValue("world", worldValue)
.withQueryValue("biome", biomeValue)
.withQueryValue("pokemon", pokemon.asStruct())
.let { if (thrower is ServerPlayer) it.withPlayerValue("player", thrower) else it }
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 value: Float) : CatchRateModifier {
companion object {
const val VARIANT = "simple"
}
override fun isGuaranteed(): Boolean = false
override fun value(thrower: LivingEntity, pokemon: Pokemon): Float = this.value
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))
}
@@ -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)
@@ -198,7 +198,17 @@ interface Spawner {
val mid = chunk.getBlockState(startPos)
val above = chunk.getBlockState(abovePos)
return above.isPathfindable(PathComputationType.AIR) && mid.isAir
// Above must be non-solid
if (!above.isPathfindable(PathComputationType.AIR)) {
return false
}
// Position must be non-air
if (mid.isAir) {
return false
}
return true
}
fun constrainArea(area: SpawningZoneInput): SpawningZoneInput? {
@@ -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,
}
}
}
@@ -117,6 +117,9 @@ abstract class ShowdownActionResponse(val type: ShowdownActionResponseType) {
data class MoveActionResponse(var moveName: String, var targetPnx: String? = null, var gimmickID: String? = null): ShowdownActionResponse(ShowdownActionResponseType.MOVE) {
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
// Ensure that the gimmick ID the player is sending is valid
gimmickID?.let { if (it !in VALID_GIMMICK_IDS) return false }
if (forceSwitch || showdownMoveSet == null) {
return false
}
@@ -168,6 +171,10 @@ data class MoveActionResponse(var moveName: String, var targetPnx: String? = nul
gimmickID = buffer.readNullable { buffer.readString() }
return this
}
companion object {
private val VALID_GIMMICK_IDS = ShowdownMoveset.Gimmick.entries.map { it.id }.toSet()
}
}
data class HealItemActionResponse(var item: String) : ShowdownActionResponse(ShowdownActionResponseType.FORCE_PASS) {
@@ -183,7 +190,8 @@ data class HealItemActionResponse(var item: String) : ShowdownActionResponse(Sho
}
override fun isValid(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?, forceSwitch: Boolean): Boolean {
return !forceSwitch
// Previously checked for !forceSwitch, excepts in the case of a forceSwitch, this couldn't legally be sent anyway.
return false
}
override fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?): String {
@@ -37,7 +37,6 @@ import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
import com.cobblemon.mod.common.util.battleLang
import com.cobblemon.mod.common.util.cobblemonResource
import java.util.concurrent.CompletableFuture
import net.minecraft.world.entity.LivingEntity
/**
* Format: |move|POKEMON|MOVE|TARGET
@@ -77,7 +76,7 @@ class MoveInstruction(
val pokemonName = userPokemon.getName()
ShowdownInterpreter.lastCauser[battle.battleId] = message
// For Spread targets the message data only gives the pnx strings. So we can't know what pokemon are actually targeted until the previous messages have been interpreted
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull() { poke -> poke.getPNX() == it }?.battlePokemon }
val spreadTargetPokemon = spreadTargets.map { battle.activePokemon.firstOrNull { poke -> poke.getPNX() == it }?.battlePokemon }
val targetPokemonEntity = targetPokemon?.entity
if (targetPokemonEntity != null) {
@@ -137,10 +136,20 @@ class MoveInstruction(
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
val faintedTargets = subsequentInstructions.filterIsInstance<FaintInstruction>().map { it.faintingPokemon }
val failed = subsequentInstructions.any { it is FailInstruction }
val failedTargets = subsequentInstructions.filterIsInstance<FailInstruction>().mapNotNull { it.target }
val hitCountInstruction = subsequentInstructions.filterIsInstance<HitCountInstruction>().firstOrNull()
runtime.environment.query.addFunction("target_fainted") { params ->
if (params.params.size == 0) {
return@addFunction DoubleValue(faintedTargets.isNotEmpty())
} else {
val entityUUID = params.getString(0)
return@addFunction DoubleValue(faintedTargets.any { it.entity?.stringUUID == entityUUID })
}
}
runtime.environment.query.addFunction("missed") { params ->
if (params.params.size == 0) {
return@addFunction DoubleValue(missedTargets.isNotEmpty())
@@ -30,7 +30,7 @@ class OrbBlock(properties: Properties) : SixFaceRotationalBlock(properties) {
val FLOOR_NORTH = Shapes.box(0.3125, 0.0, 0.3125, 0.6875, 0.375, 0.6875)
val WALL_NORTH = Shapes.box(0.3125, 0.3125, 0.625, 0.6875, 0.6875, 1.0)
val CEILING_NORTH = Shapes.box(0.3125, 0.625, 0.375, 0.6875, 1.0, 0.6875)
val CEILING_NORTH = Shapes.box(0.3125, 0.625, 0.3125, 0.6875, 1.0, 0.6875)
}
init {
@@ -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()
}
}
@@ -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() }
}
}
}
@@ -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,
@@ -25,55 +25,55 @@ object CobblemonModelPredicateRegistry {
fun registerPredicates() {
val rods = listOf(
CobblemonItems.AZURE_ROD,
CobblemonItems.BEAST_ROD,
CobblemonItems.CHERISH_ROD,
CobblemonItems.CITRINE_ROD,
CobblemonItems.DIVE_ROD,
CobblemonItems.DREAM_ROD,
CobblemonItems.DUSK_ROD,
CobblemonItems.FAST_ROD,
CobblemonItems.FRIEND_ROD,
CobblemonItems.GREAT_ROD,
CobblemonItems.HEAL_ROD,
CobblemonItems.HEAVY_ROD,
CobblemonItems.LEVEL_ROD,
CobblemonItems.LOVE_ROD,
CobblemonItems.LURE_ROD,
CobblemonItems.LUXURY_ROD,
CobblemonItems.MASTER_ROD,
CobblemonItems.MOON_ROD,
CobblemonItems.NEST_ROD,
CobblemonItems.NET_ROD,
CobblemonItems.PARK_ROD,
CobblemonItems.POKE_ROD,
CobblemonItems.PREMIER_ROD,
CobblemonItems.QUICK_ROD,
CobblemonItems.REPEAT_ROD,
CobblemonItems.ROSEATE_ROD,
CobblemonItems.SAFARI_ROD,
CobblemonItems.SLATE_ROD,
CobblemonItems.SPORT_ROD,
CobblemonItems.TIMER_ROD,
CobblemonItems.ULTRA_ROD,
CobblemonItems.VERDANT_ROD,
CobblemonItems.ANCIENT_AZURE_ROD,
CobblemonItems.ANCIENT_CITRINE_ROD,
CobblemonItems.ANCIENT_FEATHER_ROD,
CobblemonItems.ANCIENT_GIGATON_ROD,
CobblemonItems.ANCIENT_GREAT_ROD,
CobblemonItems.ANCIENT_HEAVY_ROD,
CobblemonItems.ANCIENT_IVORY_ROD,
CobblemonItems.ANCIENT_JET_ROD,
CobblemonItems.ANCIENT_LEADEN_ROD,
CobblemonItems.ANCIENT_ORIGIN_ROD,
CobblemonItems.ANCIENT_POKE_ROD,
CobblemonItems.ANCIENT_ROSEATE_ROD,
CobblemonItems.ANCIENT_SLATE_ROD,
CobblemonItems.ANCIENT_ULTRA_ROD,
CobblemonItems.ANCIENT_VERDANT_ROD,
CobblemonItems.ANCIENT_WING_ROD
val rods = listOf<PokerodItem>(
// CobblemonItems.AZURE_ROD,
// CobblemonItems.BEAST_ROD,
// CobblemonItems.CHERISH_ROD,
// CobblemonItems.CITRINE_ROD,
// CobblemonItems.DIVE_ROD,
// CobblemonItems.DREAM_ROD,
// CobblemonItems.DUSK_ROD,
// CobblemonItems.FAST_ROD,
// CobblemonItems.FRIEND_ROD,
// CobblemonItems.GREAT_ROD,
// CobblemonItems.HEAL_ROD,
// CobblemonItems.HEAVY_ROD,
// CobblemonItems.LEVEL_ROD,
// CobblemonItems.LOVE_ROD,
// CobblemonItems.LURE_ROD,
// CobblemonItems.LUXURY_ROD,
// CobblemonItems.MASTER_ROD,
// CobblemonItems.MOON_ROD,
// CobblemonItems.NEST_ROD,
// CobblemonItems.NET_ROD,
// CobblemonItems.PARK_ROD,
// CobblemonItems.POKE_ROD,
// CobblemonItems.PREMIER_ROD,
// CobblemonItems.QUICK_ROD,
// CobblemonItems.REPEAT_ROD,
// CobblemonItems.ROSEATE_ROD,
// CobblemonItems.SAFARI_ROD,
// CobblemonItems.SLATE_ROD,
// CobblemonItems.SPORT_ROD,
// CobblemonItems.TIMER_ROD,
// CobblemonItems.ULTRA_ROD,
// CobblemonItems.VERDANT_ROD,
// CobblemonItems.ANCIENT_AZURE_ROD,
// CobblemonItems.ANCIENT_CITRINE_ROD,
// CobblemonItems.ANCIENT_FEATHER_ROD,
// CobblemonItems.ANCIENT_GIGATON_ROD,
// CobblemonItems.ANCIENT_GREAT_ROD,
// CobblemonItems.ANCIENT_HEAVY_ROD,
// CobblemonItems.ANCIENT_IVORY_ROD,
// CobblemonItems.ANCIENT_JET_ROD,
// CobblemonItems.ANCIENT_LEADEN_ROD,
// CobblemonItems.ANCIENT_ORIGIN_ROD,
// CobblemonItems.ANCIENT_POKE_ROD,
// CobblemonItems.ANCIENT_ROSEATE_ROD,
// CobblemonItems.ANCIENT_SLATE_ROD,
// CobblemonItems.ANCIENT_ULTRA_ROD,
// CobblemonItems.ANCIENT_VERDANT_ROD,
// CobblemonItems.ANCIENT_WING_ROD
)
rods.forEach { rod ->
@@ -186,4 +186,4 @@ object CobblemonModelPredicateRegistry {
}
*/
}
}
}
@@ -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)
}
}
}
}
@@ -29,8 +29,8 @@ class FarfetchdGalarianModel(root: ModelPart) : PokemonPosableModel(root), Heade
override var portraitScale = 2.6F
override var portraitTranslation = Vec3(-0.2, -1.0, 0.0)
override var profileScale = 1.1F
override var profileTranslation = Vec3(-0.1, 0.1, 0.0)
override var profileScale = 0.93F
override var profileTranslation = Vec3(-0.1, 0.36, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
@@ -28,8 +28,8 @@ class FarfetchdModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
override var portraitScale = 2.6F
override var portraitTranslation = Vec3(-0.2, -1.0, 0.0)
override var profileScale = 1.1F
override var profileTranslation = Vec3(-0.1, 0.1, 0.0)
override var profileScale = 0.93F
override var profileTranslation = Vec3(-0.1, 0.36, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
@@ -23,8 +23,8 @@ class KabutoModel(root: ModelPart) : PokemonPosableModel(root) {
override var portraitScale = 3.3F
override var portraitTranslation = Vec3(0.0, -3.3, 0.0)
override var profileScale = 1.6F
override var profileTranslation = Vec3(0.0, -0.6, 0.0)
override var profileScale = 1.25F
override var profileTranslation = Vec3(0.0, -0.17, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
@@ -21,8 +21,8 @@ class HoothootModel (root: ModelPart) : PokemonPosableModel(root) {
override var portraitScale = 2.2F
override var portraitTranslation = Vec3(-0.25, -0.9, 0.0)
override var profileScale = 1.0F
override var profileTranslation = Vec3(0.0, 0.3, 0.0)
override var profileScale = 0.82F
override var profileTranslation = Vec3(0.0, 0.53, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
@@ -29,8 +29,8 @@ class WooperModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.15, -0.65, 0.0)
override var profileScale = 0.9F
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
override var profileScale = 0.75F
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
@@ -29,8 +29,8 @@ class WooperPaldeanModel(root: ModelPart) : PokemonPosableModel(root), HeadedFra
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.15, -0.65, 0.0)
override var profileScale = 0.9F
override var profileTranslation = Vec3(0.0, 0.4, 0.0)
override var profileScale = 0.75F
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
@@ -34,7 +34,7 @@ class DusclopsModel (root: ModelPart) : PokemonPosableModel(root) {
lateinit var battleidle: Pose
override fun registerPoses() {
animations["cry"] = "q.bedrock_stateful('squirtle', 'cry')".asExpressionLike()
animations["cry"] = "q.bedrock_stateful('dusclops', 'cry')".asExpressionLike()
val blink = quirk { bedrockStateful("dusclops", "blink") }
@@ -23,7 +23,7 @@ class CarbinkModel (root: ModelPart) : PokemonPosableModel(root) {
override var portraitScale = 1.8F
override var portraitTranslation = Vec3(0.0, 0.2, 0.0)
override var profileScale = 0.85F
override var profileScale = 0.68F
override var profileTranslation = Vec3(0.0, 0.7, 0.0)
lateinit var standing: CobblemonPose
@@ -26,8 +26,8 @@ class ScatterbugModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame
override var portraitScale = 2.6F
override var portraitTranslation = Vec3(-0.2, -1.9, 0.0)
override var profileScale = 1.2F
override var profileTranslation = Vec3(0.0, 0.05, 0.0)
override var profileScale = 0.98F
override var profileTranslation = Vec3(0.0, 0.28, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
@@ -36,8 +36,8 @@ class CharcadetModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
override var portraitScale = 1.7F
override var portraitTranslation = Vec3(-0.15, 0.08, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.6, 0.0)
override var profileScale = 0.58F
override var profileTranslation = Vec3(0.0, 0.91, 0.0)
lateinit var standing: CobblemonPose
lateinit var walking: CobblemonPose
@@ -35,8 +35,8 @@ class QuaquavalModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame,
override var portraitScale = 2.3F
override var portraitTranslation = Vec3(-0.42, 3.7, 0.0)
override var profileScale = 0.32F
override var profileTranslation = Vec3(0.0, 1.24, 0.0)
override var profileScale = 0.36F
override var profileTranslation = Vec3(0.0, 1.19, 0.0)
lateinit var standing: CobblemonPose
lateinit var standing2: CobblemonPose
@@ -1,208 +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.gen9
import com.cobblemon.mod.common.client.render.models.blockbench.animation.WingFlapIdleAnimation
import com.cobblemon.mod.common.client.render.models.blockbench.createTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BiWingedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BipedFrame
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.client.render.models.blockbench.wavefunction.parabolaFunction
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.sineFunction
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.math.geometry.toRadians
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SquawkabillyModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BiWingedFrame {
override val rootPart = root.registerChildWithAllChildren("squawkabilly")
override val head = getPart("head")
override val leftWing = getPart("wing_left")
override val rightWing = getPart("wing_right")
override val leftLeg = getPart("leg_left")
override val rightLeg = getPart("leg_right")
private val tail = getPart("tail")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.2, -0.2, 0.0)
override var profileScale = 0.85F
override var profileTranslation = Vec3(0.0, 0.51, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hovering: Pose
lateinit var flying: Pose
lateinit var shoulderLeft: Pose
lateinit var shoulderRight: Pose
override val cryAnimation = CryProvider { bedrockStateful("squawkabilly", "cry") }
val shoulderOffsetX = -1.0
val shoulderOffsetY = 0
override fun registerPoses() {
val blink = quirk { bedrockStateful("squawkabilly", "blink") }
standing = registerPose(
poseName = "standing",
quirks = arrayOf(blink),
poseTypes = PoseType.UI_POSES + PoseType.STAND,
transformTicks = 10,
animations = arrayOf(
singleBoneLook(),
bedrock("squawkabilly", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
quirks = arrayOf(blink),
poseType = PoseType.WALK,
transformTicks = 10,
animations = arrayOf(
singleBoneLook(),
bedrock("squawkabilly", "ground_idle"),
rootPart.translation(
function = parabolaFunction(
peak = -4F,
period = 0.4F
),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Y_AXIS
),
head.translation(
function = sineFunction(
amplitude = (-20F).toRadians(),
period = 1F,
verticalShift = (-10F).toRadians()
),
axis = ModelPartTransformation.X_AXIS,
timeVariable = { state, _, _ -> state.animationSeconds }
),
leftLeg.rotation(
function = parabolaFunction(
tightness = -20F,
phaseShift = 0F,
verticalShift = (30F).toRadians()
),
axis = ModelPartTransformation.X_AXIS,
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
),
rightLeg.rotation(
function = parabolaFunction(
tightness = -20F,
phaseShift = 0F,
verticalShift = (30F).toRadians()
),
axis = ModelPartTransformation.X_AXIS,
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
),
tail.rotation(
function = sineFunction(
amplitude = (-5F).toRadians(),
period = 1F
),
axis = ModelPartTransformation.X_AXIS,
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
),
wingFlap(
flapFunction = sineFunction(
amplitude = (-5F).toRadians(),
period = 0.4F,
phaseShift = 0.00F,
verticalShift = (-20F).toRadians()
),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Z_AXIS
),
rightWing.translation(
function = parabolaFunction(
tightness = -10F,
phaseShift = 30F,
verticalShift = (25F).toRadians()
),
axis = ModelPartTransformation.Y_AXIS,
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
),
leftWing.translation(
function = parabolaFunction(
tightness = -10F,
phaseShift = 30F,
verticalShift = (25F).toRadians()
),
axis = ModelPartTransformation.Y_AXIS,
timeVariable = { _, _, ageInTicks -> ageInTicks / 20 },
),
)
)
hovering = registerPose(
poseName = "hovering",
quirks = arrayOf(blink),
poseTypes = setOf(PoseType.FLOAT, PoseType.HOVER),
transformTicks = 10,
animations = arrayOf(
singleBoneLook(),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -10F.toRadians(), period = 0.9F, amplitude = 0.9F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Z_AXIS
)
)
)
flying = registerPose(
poseName = "flying",
quirks = arrayOf(blink),
poseTypes = setOf(PoseType.FLY, PoseType.SWIM),
transformTicks = 10,
animations = arrayOf(
singleBoneLook(),
WingFlapIdleAnimation(this,
flapFunction = sineFunction(verticalShift = -14F.toRadians(), period = 0.9F, amplitude = 0.9F),
timeVariable = { state, _, _ -> state.animationSeconds },
axis = ModelPartTransformation.Z_AXIS
)
)
)
shoulderLeft = registerPose(
poseType = PoseType.SHOULDER_LEFT,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("squawkabilly", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(shoulderOffsetX, shoulderOffsetY, 0.0)
)
)
shoulderRight = registerPose(
poseType = PoseType.SHOULDER_RIGHT,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("squawkabilly", "ground_idle")
),
transformedParts = arrayOf(
rootPart.createTransformation().addPosition(-shoulderOffsetX, shoulderOffsetY, 0.0)
)
)
}
// override fun getFaintAnimation(
// pokemonEntity: PokemonEntity,
// state: PosableState<PokemonEntity>
// ) = if (state.isNotPosedIn(sleep)) bedrockStateful("squawkabilly", "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,55 +168,6 @@ 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("metapod", ::MetapodModel)
@@ -397,7 +347,6 @@ object VaryingModelRepository {
inbuilt("bunnelby", ::BunnelbyModel)
inbuilt("arrokuda", ::ArrokudaModel)
inbuilt("barraskewda", ::BarraskewdaModel)
inbuilt("squawkabilly", ::SquawkabillyModel)
inbuilt("ponyta_galarian", ::PonytaGalarianModel)
inbuilt("rapidash_galarian", ::RapidashGalarianModel)
inbuilt("volbeat", ::VolbeatModel)
@@ -667,4 +616,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
@@ -320,6 +322,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 +392,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,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()
)
@@ -284,7 +301,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
}
@@ -296,7 +313,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
delegate.tick(this)
if (level().isServerSide()) {
if (this.tickCount > 600 && this.capturingPokemon == null) {
this.remove(RemovalReason.DISCARDED)
@@ -346,7 +363,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
@@ -355,8 +373,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
@@ -380,7 +403,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')"))
@@ -395,7 +418,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
@@ -425,8 +448,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"))
@@ -443,7 +465,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)
@@ -551,4 +573,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(var 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
@@ -942,7 +944,12 @@ object CobblemonItemGroups {
}
private fun utilityItemEntries(displayContext: ItemDisplayParameters, entries: Output) {
CobblemonItems.pokeBalls.forEach(entries::accept)
CobblemonItems.pokeBalls.values.forEach(entries::accept)
PokeBalls.all()
.filter { ball -> CobblemonItems.pokeBalls.keys.none { it == ball.name } }
.forEach { entries.accept(PokeBallComponent.createStack(it)) }
CobblemonItems.pokeRods.forEach(entries::accept)
CobblemonItems.pokedexes.forEach(entries::accept)
}
@@ -0,0 +1,70 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.net.messages.client.pokeballs
import com.cobblemon.mod.common.api.pokeball.PokeBalls
import com.cobblemon.mod.common.net.messages.client.data.DataRegistrySyncPacket
import com.cobblemon.mod.common.pokeball.PokeBall
import com.cobblemon.mod.common.util.adapters.CobblemonCatchRateModifierAdapter
import com.cobblemon.mod.common.util.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.cobblemonResource
import net.minecraft.network.RegistryFriendlyByteBuf
import net.minecraft.resources.ResourceLocation
import java.awt.Color
class PokeBallsRegistrySyncPacket(balls: Map<ResourceLocation, PokeBall>) : DataRegistrySyncPacket<PokeBall, PokeBallsRegistrySyncPacket>(balls.values) {
companion object {
val ID = cobblemonResource("poke_balls")
fun decode(buffer: RegistryFriendlyByteBuf): PokeBallsRegistrySyncPacket {
return PokeBallsRegistrySyncPacket(emptyMap()).apply { decodeBuffer(buffer) }
}
}
override val id = ID
override fun encodeEntry(buffer: RegistryFriendlyByteBuf, entry: PokeBall) {
buffer.writeResourceLocation(entry.name)
buffer.writeResourceLocation(entry.model2d)
buffer.writeResourceLocation(entry.model3d)
buffer.writeFloat(entry.throwPower)
buffer.writeFloat(entry.waterDragValue)
buffer.writeBoolean(entry.ancient)
buffer.writeInt(entry.beamColor.rgb)
CobblemonCatchRateModifierAdapter.encode(entry.catchRateModifier, buffer)
}
override fun decodeEntry(buffer: RegistryFriendlyByteBuf): PokeBall {
val name = buffer.readResourceLocation()
val model2d = buffer.readResourceLocation()
val model3d = buffer.readResourceLocation()
val throwPower = buffer.readFloat()
val waterDrag = buffer.readFloat()
val ancient = buffer.readBoolean()
val color = Color(buffer.readInt())
val catchRateModifier = CobblemonCatchRateModifierAdapter.decode(buffer)
return PokeBall(
name = name,
model2d = model2d,
model3d = model3d,
throwPower = throwPower,
waterDragValue = waterDrag,
ancient = ancient,
beamColor = color,
catchRateModifier = catchRateModifier,
effects = listOf(),
)
}
override fun synchronizeDecoded(entries: Collection<PokeBall>) {
PokeBalls.reload(entries.associateBy { it.name }, RegistryAccessResolver.getAccess())
}
}
@@ -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)
}
}
}
}
@@ -8,9 +8,12 @@
package com.cobblemon.mod.common.net.serverhandling.battle
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.net.ServerNetworkPacketHandler
import com.cobblemon.mod.common.api.text.red
import com.cobblemon.mod.common.battles.BattleRegistry
import com.cobblemon.mod.common.battles.HealItemActionResponse
import com.cobblemon.mod.common.battles.MoveActionResponse
import com.cobblemon.mod.common.exception.IllegalActionChoiceException
import com.cobblemon.mod.common.net.messages.client.battle.BattleMakeChoicePacket
import com.cobblemon.mod.common.net.messages.client.battle.BattleQueueRequestPacket
@@ -25,6 +28,27 @@ object BattleSelectActionsHandler : ServerNetworkPacketHandler<BattleSelectActio
if (!actor.mustChoose) {
return
}
// Battle command injection attempt detection
packet.showdownActionResponses.asSequence()
.flatMap {
when (it) {
is MoveActionResponse -> sequenceOf(it.moveName, it.targetPnx, it.gimmickID)
is HealItemActionResponse -> sequenceOf(it.item)
else -> emptySequence()
}
}
.filterNotNull()
.firstOrNull { field -> field.any(Char::isISOControl) }
?.let {
// Escapes control chars as \uXXXX to prevent forging logs.
val printable = it.replace(Regex("\\p{Cc}")) { "\\u%04x".format(it.value[0].code) }
Cobblemon.LOGGER.warn("Blocked battle command injection from ${player.name.string}: $printable")
actor.sendUpdate(BattleQueueRequestPacket(actor.request!!))
actor.sendUpdate(BattleMakeChoicePacket())
return
}
try {
actor.setActionResponses(packet.showdownActionResponses)
} catch (e: IllegalActionChoiceException) {
@@ -9,17 +9,25 @@
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.MoLangFunctions.asMoLangValue
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.adapters.RegistryAccessResolver
import com.cobblemon.mod.common.util.cobblemonResource
import com.cobblemon.mod.common.util.codec.CodecUtils
import com.google.gson.annotations.SerializedName
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 +39,54 @@ 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 @JvmOverloads constructor(
@Transient // Assigns in PokeBalls.reload
var name: ResourceLocation = cobblemonResource("strange_ball"),
@SerializedName("modifier")
val catchRateModifier: CatchRateModifier = CatchRateModifier.DUMMY,
val effects: List<CaptureEffect> = listOf(),
val effects: List<ExpressionLike> = listOf(),
@SerializedName("item_properties")
val itemProperties: PokeBallProperties = PokeBallProperties(),
@SerializedName("water_drag")
val waterDragValue: Float,
@SerializedName("model_2d")
val model2d: ResourceLocation,
@SerializedName("model_3d")
val model3d: ResourceLocation,
@SerializedName("throw_power")
val throwPower: Float,
val ancient: Boolean
val ancient: Boolean = false,
@SerializedName("beam_color")
val beamColor: Color = Color.RED,
) {
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) }
// .addFunction("item") { } // requires a registry which is hard
@delegate:Transient
val struct: QueryStruct by lazy {
ObjectValue(this)
.addPokeBallFunctions(this)
.addFunction("stack") { this.stack().asMoLangValue(RegistryAccessResolver.getAccess()) }
}
// 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 = Item.Properties()
.rarity(this.itemProperties.rarity)
.let { if (itemProperties.fireResistant) it.fireResistant() else it }
@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 +95,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 {
}
}
}
}
@@ -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))
@@ -19,6 +19,8 @@ import java.nio.file.SimpleFileVisitor
import java.nio.file.attribute.BasicFileAttributes
import kotlin.io.path.toPath
import net.minecraft.resources.ResourceLocation
import kotlin.io.path.name
import kotlin.text.endsWith
/**
* Basic functions for dealing with assets inside the mod, using the standard file visitor
@@ -29,7 +31,9 @@ import net.minecraft.resources.ResourceLocation
*/
object AssetLoading {
fun ResourceLocation.toPath() = toURL()?.toPath()
fun ResourceLocation.toURL() = Cobblemon::class.java.getResource(String.format("/assets/%s/%s", namespace, path))?.toURI()
fun ResourceLocation.toURL() =
Cobblemon::class.java.getResource(String.format("/assets/%s/%s", namespace, path))?.toURI()
fun fileSearch(dir: Path, filter: (Path) -> Boolean, recursive: Boolean): List<Path> {
val files = mutableListOf<Path>()
Files.walkFileTree(dir, object : SimpleFileVisitor<Path>() {
@@ -58,4 +62,14 @@ object AssetLoading {
}
}
}
}
/**
* very "clever" way to get all files in asset folder in loaded minecraft client env
*
*/
fun allAtPath(dir: Path, recursive: Boolean = false, suffix: String = ".json", take: Int = 2) = fileSearch(
dir = dir,
recursive = recursive,
filter = { path -> path.name.endsWith(suffix) },
).map { it.toString().removeSuffix(suffix).split("/").takeLast(take).joinToString("/") }
}
@@ -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
}
}
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2026. gitoido-mc
* This Source Code Form is subject to the terms of the GNU General Public License v3.0.
* If a copy of the GNU General Public License v3.0 was not distributed with this file,
* you can obtain one at https://github.com/gitoido-mc/conditional-riding/blob/main/LICENSE.
*/
package com.cobblemon.mod.common.util.adapters
import com.google.gson.Gson
import com.google.gson.TypeAdapter
import com.google.gson.TypeAdapterFactory
import com.google.gson.reflect.TypeToken
import com.google.gson.stream.JsonReader
import com.google.gson.stream.JsonToken
import com.google.gson.stream.JsonWriter
import java.io.IOException
import java.lang.reflect.ParameterizedType
import java.util.*
class OptionalTypeAdapterFactory : TypeAdapterFactory {
class OptionalTypeAdapter<T>(
private val typeAdapter: TypeAdapter<T>,
) : TypeAdapter<Optional<T>>() {
@Throws(IOException::class)
override fun write(
out: JsonWriter,
value: Optional<T>,
) {
when (value.isPresent) {
true -> typeAdapter.write(out, value.get())
false -> out.nullValue()
}
}
@Throws(IOException::class)
override fun read(`in`: JsonReader): Optional<T> =
when (`in`.peek() != JsonToken.NULL) {
true -> {
Optional.ofNullable(typeAdapter.read(`in`))
}
false -> {
`in`.nextNull()
Optional.empty()
}
} as Optional<T>
}
override fun <T> create(
gson: Gson,
typeToken: TypeToken<T>,
): TypeAdapter<T>? {
if (!Optional::class.java.isAssignableFrom(typeToken.rawType)) return null
val optionalType = typeToken.type
val parametrizedType = optionalType as ParameterizedType
val typeArgs = parametrizedType.actualTypeArguments
val inner = TypeToken.get(typeArgs.first())
val innerAdapter = gson.getAdapter(inner)
return OptionalTypeAdapter(innerAdapter) as TypeAdapter<T>?
}
}
@@ -0,0 +1,63 @@
/*
* 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.CatchRateModifier
import com.cobblemon.mod.common.api.spawning.detail.PossibleHeldItem
import com.cobblemon.mod.common.pokeball.PokeBall
import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement
import com.google.gson.JsonObject
import com.mojang.serialization.Codec
import com.mojang.serialization.JsonOps
import net.minecraft.client.renderer.item.ItemProperties
import net.minecraft.core.component.DataComponentMap
import net.minecraft.resources.ResourceLocation
import java.awt.Color
import java.lang.reflect.Type
import kotlin.jvm.optionals.getOrNull
/**
* A JSON deserializer for [PokeBall].
*
* @author Gito
* @since October 28th, 2026
*/
object PokeBallAdapter : JsonDeserializer<PokeBall> {
override fun deserialize(json: JsonElement, tp: Type, ctx: JsonDeserializationContext): PokeBall {
json as JsonObject
val beamColor: Color = when(json.has("beam_color")) {
true -> Color.decode(json.get("beam_color").asString)
false -> Color.RED
}
val properties: PokeBall.PokeBallProperties = when(json.has("item_properties")) {
true -> ctx.deserialize(json.get("item_properties"), PokeBall.PokeBallProperties::class.java)
false -> PokeBall.PokeBallProperties()
}
return PokeBall(
catchRateModifier = ctx.deserialize(json.get("modifier"), CatchRateModifier::class.java),
effects = listOf(),
itemProperties = properties,
waterDragValue = tryParse(json.get("water_drag"), Codec.FLOAT),
model2d = tryParse(json.get("model_2d"), ResourceLocation.CODEC),
model3d = tryParse(json.get("model_3d"), ResourceLocation.CODEC),
throwPower = tryParse(json.get("throw_power"), Codec.FLOAT),
ancient = if (json.has("ancient")) json.get("ancient").asBoolean else false,
beamColor = beamColor,
)
}
private fun <T> tryParse(json: JsonElement, codec: Codec<T>): T {
return runCatching { JsonOps.INSTANCE.withDecoder(codec).apply(json).result().getOrNull()!!.first }.getOrThrow()
}
}
@@ -0,0 +1,90 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_actor",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/moves/watershuriken"
}
},
"curves": {
"variable.rsize": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
}
},
"events": {
"trail": {
"particle_effect": {
"effect": "cobblemon:watershuriken_actortrail",
"type": "particle"
}
},
"hit": {
"particle_effect": {
"effect": "cobblemon:watershuriken_target",
"type": "particle"
}
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.target_deltaz=5;"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 0.25
},
"minecraft:emitter_shape_disc": {
"offset": [0, 0.02, 0.1],
"radius": 0.5,
"direction": "outwards"
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.35
},
"minecraft:particle_lifetime_events": {
"timeline": {
"0.00": "trail",
"0.06": "trail",
"0.12": "trail",
"0.18": "trail",
"0.24": "trail",
"0.30": "trail",
"0.34": "hit"
}
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(0,3)*90"
},
"minecraft:particle_motion_parametric": {
"relative_position": ["-v.target_deltax*(v.particle_age / v.particle_lifetime)", "(v.target_deltay*(v.particle_age / v.particle_lifetime))", "-v.target_deltaz*(v.particle_age / v.particle_lifetime)"]
},
"minecraft:particle_appearance_billboard": {
"size": ["0.4*v.rsize", "0.4*v.rsize"],
"facing_camera_mode": "lookat_direction",
"direction": {
"mode": "custom",
"custom_direction": [0, 0, -1]
},
"uv": {
"texture_width": 60,
"texture_height": 15,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [15, 15],
"step_UV": [15, 0],
"frames_per_second": 24,
"max_frame": 4,
"loop": true
}
}
}
}
}
}
@@ -0,0 +1,91 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_actoralt",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/moves/watershuriken"
}
},
"curves": {
"variable.rsize": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
}
},
"events": {
"trail": {
"particle_effect": {
"effect": "cobblemon:watershuriken_actortrail",
"type": "particle"
}
},
"hit": {
"particle_effect": {
"effect": "cobblemon:watershuriken_target",
"type": "particle"
}
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.target_deltaz=5;"
},
"minecraft:emitter_rate_steady": {
"spawn_rate": 7,
"max_particles": 3
},
"minecraft:emitter_lifetime_once": {
"active_time": 0.4
},
"minecraft:emitter_shape_disc": {
"offset": [0, 0.02, 0.1],
"radius": 0.5,
"direction": "outwards"
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.35
},
"minecraft:particle_lifetime_events": {
"timeline": {
"0.00": "trail",
"0.06": "trail",
"0.12": "trail",
"0.18": "trail",
"0.24": "trail",
"0.30": "trail",
"0.34": "hit"
}
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(0,3)*90"
},
"minecraft:particle_motion_parametric": {
"relative_position": ["-(v.target_deltax+((v.particle_random_1-0.5)*1.33))*(v.particle_age / v.particle_lifetime)", "(v.target_deltay+((v.particle_random_2-0.5)*1.33))*(v.particle_age / v.particle_lifetime)", "-v.target_deltaz*(v.particle_age / v.particle_lifetime)"]
},
"minecraft:particle_appearance_billboard": {
"size": ["0.4*v.rsize", "0.4*v.rsize"],
"facing_camera_mode": "lookat_direction",
"direction": {
"mode": "custom",
"custom_direction": [0, 0, -1]
},
"uv": {
"texture_width": 60,
"texture_height": 15,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [15, 15],
"step_UV": [15, 0],
"frames_per_second": 24,
"max_frame": 4,
"loop": true
}
}
}
}
}
}
@@ -0,0 +1,86 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_actorhor",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/moves/watershuriken"
}
},
"curves": {
"variable.rsize": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
}
},
"events": {
"trail": {
"particle_effect": {
"effect": "cobblemon:watershuriken_actortrail",
"type": "particle"
}
},
"hit": {
"particle_effect": {
"effect": "cobblemon:watershuriken_target",
"type": "particle"
}
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.target_deltaz=5;"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 0.25
},
"minecraft:emitter_shape_disc": {
"offset": [0, 0.02, 0.1],
"radius": 0.5,
"direction": "outwards"
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.35
},
"minecraft:particle_lifetime_events": {
"timeline": {
"0.00": "trail",
"0.06": "trail",
"0.12": "trail",
"0.18": "trail",
"0.24": "trail",
"0.30": "trail",
"0.34": "hit"
}
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(0,3)*90"
},
"minecraft:particle_motion_parametric": {
"relative_position": ["-v.target_deltax*(v.particle_age / v.particle_lifetime)", "(v.target_deltay*(v.particle_age / v.particle_lifetime))", "-v.target_deltaz*(v.particle_age / v.particle_lifetime)"]
},
"minecraft:particle_appearance_billboard": {
"size": ["0.4*v.rsize", "0.4*v.rsize"],
"facing_camera_mode": "emitter_transform_xz",
"uv": {
"texture_width": 60,
"texture_height": 15,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [15, 15],
"step_UV": [15, 0],
"frames_per_second": 24,
"max_frame": 4,
"loop": true
}
}
}
}
}
}
@@ -0,0 +1,63 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_actortrail",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/impact/impact_water"
}
},
"curves": {
"variable.scale": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [1, 0]
}
},
"components": {
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"direction": ["v.particle_random_1-0.5", "v.particle_random_2-0.25", -3]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": "math.random(0.3, 0.6)"
},
"minecraft:particle_initial_spin": {
"rotation": "math.round(v.particle_random_4*3)*90"
},
"minecraft:particle_initial_speed": 8,
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, -5, 0],
"linear_drag_coefficient": 8
},
"minecraft:particle_appearance_billboard": {
"size": ["0.22 * variable.scale", "0.22 * variable.scale"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 8,
"texture_height": 56,
"flipbook": {
"base_UV": [0, 24],
"size_UV": [8, 8],
"step_UV": [0, 8],
"frames_per_second": 16,
"max_frame": 4
}
}
},
"minecraft:particle_motion_collision": {
"collision_radius": 0.02
},
"minecraft:particle_appearance_tinting": {
"color": [0.61176, 0.87451, 1, 1]
}
}
}
}
@@ -0,0 +1,86 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_actorvert",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/moves/watershuriken"
}
},
"curves": {
"variable.rsize": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [0, 1, 1, 1, 1, 1, 1, 1]
}
},
"events": {
"trail": {
"particle_effect": {
"effect": "cobblemon:watershuriken_actortrail",
"type": "particle"
}
},
"hit": {
"particle_effect": {
"effect": "cobblemon:watershuriken_target",
"type": "particle"
}
}
},
"components": {
"minecraft:emitter_initialization": {
"creation_expression": "v.target_deltaz=5;"
},
"minecraft:emitter_rate_instant": {
"num_particles": 1
},
"minecraft:emitter_lifetime_once": {
"active_time": 0.25
},
"minecraft:emitter_shape_disc": {
"offset": [0, 0.02, 0.1],
"radius": 0.5,
"direction": "outwards"
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": 0.35
},
"minecraft:particle_lifetime_events": {
"timeline": {
"0.00": "trail",
"0.06": "trail",
"0.12": "trail",
"0.18": "trail",
"0.24": "trail",
"0.30": "trail",
"0.34": "hit"
}
},
"minecraft:particle_initial_spin": {
"rotation": "math.random_integer(0,3)*90"
},
"minecraft:particle_motion_parametric": {
"relative_position": ["-v.target_deltax*(v.particle_age / v.particle_lifetime)", "(v.target_deltay*(v.particle_age / v.particle_lifetime))", "-v.target_deltaz*(v.particle_age / v.particle_lifetime)"]
},
"minecraft:particle_appearance_billboard": {
"size": ["0.4*v.rsize", "0.4*v.rsize"],
"facing_camera_mode": "emitter_transform_yz",
"uv": {
"texture_width": 60,
"texture_height": 15,
"flipbook": {
"base_UV": [0, 0],
"size_UV": [15, 15],
"step_UV": [15, 0],
"frames_per_second": 24,
"max_frame": 4,
"loop": true
}
}
}
}
}
}
@@ -0,0 +1,69 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_target",
"basic_render_parameters": {
"material": "particles_alpha",
"texture": "textures/particles/generic/impact/impact_water"
}
},
"curves": {
"variable.scale": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [1, 0]
}
},
"events": {
"event": {
"particle_effect": {
"effect": "cobblemon:watershuriken_targetspark",
"type": "particle"
}
}
},
"components": {
"minecraft:emitter_rate_steady": {
"spawn_rate": 240,
"max_particles": 14
},
"minecraft:emitter_lifetime_once": {
"active_time": 0.1
},
"minecraft:emitter_lifetime_events": {
"creation_event": "event"
},
"minecraft:emitter_shape_point": {},
"minecraft:particle_lifetime_expression": {
"max_lifetime": "math.random(0.3, 0.6)"
},
"minecraft:particle_initial_spin": {
"rotation": "math.round(v.particle_random_4*3)*90"
},
"minecraft:particle_initial_speed": "math.random(9,17)",
"minecraft:particle_motion_dynamic": {
"linear_drag_coefficient": 5
},
"minecraft:particle_appearance_billboard": {
"size": ["0.3 * variable.scale", "0.3 * variable.scale"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 8,
"texture_height": 56,
"flipbook": {
"base_UV": [0, 16],
"size_UV": [8, 8],
"step_UV": [0, 8],
"frames_per_second": 16,
"max_frame": 5
}
}
},
"minecraft:particle_appearance_tinting": {
"color": [0.61176, 0.87451, 1, 1]
}
}
}
}
@@ -0,0 +1,62 @@
{
"format_version": "1.10.0",
"particle_effect": {
"description": {
"identifier": "cobblemon:watershuriken_targetspark",
"basic_render_parameters": {
"material": "particles_blend",
"texture": "textures/particles/vanilla/critical_hit"
}
},
"curves": {
"variable.bitespark": {
"type": "linear",
"input": "v.particle_age",
"horizontal_range": "v.particle_lifetime",
"nodes": [0, 1, 0.88, 0.66, 0.35, -0.05]
}
},
"components": {
"minecraft:emitter_rate_instant": {
"num_particles": 7
},
"minecraft:emitter_lifetime_once": {
"active_time": 1
},
"minecraft:emitter_shape_point": {
"offset": [0, 0, -0.25],
"direction": ["v.particle_random_1-0.5", "v.particle_random_2-0.25", "v.particle_random_3-0.5"]
},
"minecraft:particle_lifetime_expression": {
"max_lifetime": "0.5-(v.particle_random_4*0.1)"
},
"minecraft:particle_initial_speed": "math.random(1.75,3.55)*3",
"minecraft:particle_motion_dynamic": {
"linear_acceleration": [0, -5, 0],
"linear_drag_coefficient": 4
},
"minecraft:particle_appearance_billboard": {
"size": ["0.125*v.bitespark", "0.125*v.bitespark"],
"facing_camera_mode": "rotate_xyz",
"uv": {
"texture_width": 8,
"texture_height": 8,
"uv": [0, 0],
"uv_size": [7, 7]
}
},
"minecraft:particle_motion_collision": {
"collision_radius": 0.01
},
"minecraft:particle_appearance_tinting": {
"color": {
"interpolant": "v.particle_age / v.particle_lifetime",
"gradient": {
"0.0": "#FF51D8FF",
"1.0": "#FF0076FF"
}
}
}
}
}
}
@@ -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')"
}
}
}
}
@@ -37,10 +37,10 @@
"rotation": [0, -7.5, 0]
},
"tail2": {
"rotation": [30, 0, 0]
"rotation": ["30-math.sin(q.anim_time*90*2-60)*-3", 0, 0]
},
"tail3": {
"rotation": [12.5, 0, 0]
"rotation": ["12.5-math.sin(q.anim_time*90*2-120)*-3", 0, 0]
},
"leg_right": {
"rotation": [0, 5, 0]
@@ -53,6 +53,22 @@
},
"wings_open": {
"scale": 0
},
"torso": {
"rotation": ["-math.sin(q.anim_time*90*2-90)*1", 0, "math.sin(q.anim_time*90*1-90)*1"],
"position": [0, "math.sin(q.anim_time*90*2)*0.1", 0]
},
"wing_closed_left": {
"rotation": ["-0-math.sin(q.anim_time*90*2-150)*1", 0, 0]
},
"wing_closed_right": {
"rotation": ["-0-math.sin(q.anim_time*90*2-150)*1", 0, 0]
},
"neck": {
"rotation": ["-math.sin(q.anim_time*90*2-180)*1", 0, 0]
},
"head": {
"rotation": ["-math.sin(q.anim_time*90*2-90)*-2", 0, "math.sin(q.anim_time*90*1-60)*-1"]
}
}
},
@@ -111,6 +127,30 @@
}
}
},
"torso": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.1667": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"jaw": {
"rotation": {
"0.0": {
@@ -118,19 +158,15 @@
"lerp_mode": "catmullrom"
},
"0.1667": {
"post": [85, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4167": {
"post": [85, 0, 0],
"post": [72.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": {
"post": [85, 0, 0],
"0.8333": {
"post": [52.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.1667": {
@@ -138,6 +174,32 @@
"lerp_mode": "catmullrom"
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.5833": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"0.75": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.5833": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"0.75": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
}
},
"sound_effects": {
@@ -203,6 +265,546 @@
"position": [-0.1, 0, -0.1]
}
}
},
"animation.spearow.hold_item": {
"loop": true,
"bones": {
"jaw": {
"rotation": [30, 0, 0]
}
}
},
"animation.spearow.ground_walk": {
"loop": true,
"animation_length": 0.75,
"bones": {
"torso": {
"rotation": ["1-math.cos(q.anim_time*90*16/3*2)*-2", "-math.sin(q.anim_time*90*16/3+60)*3", "-1+math.sin(q.anim_time*90*16/3+30)*-4"],
"position": [0, "math.sin(q.anim_time*90*16/3*2)*0.2", 0]
},
"wings_open": {
"scale": 0
},
"wing_closed_left": {
"rotation": [3, 0, 0]
},
"wing_closed_right": {
"rotation": [3, 0, 0]
},
"tail2": {
"rotation": ["22-math.cos(q.anim_time*90*16/3*2)*5", 0, 0]
},
"tail3": {
"rotation": ["5-math.cos(q.anim_time*90*16/3*2)*5", 0, 0]
},
"neck": {
"rotation": {
"0.0": ["-math.cos(q.anim_time*90*16/3*2)*-12.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"],
"0.125": ["-math.cos(q.anim_time*90*16/3*2)*-9.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"],
"0.1667": ["-math.cos(q.anim_time*90*16/3*2)*-12.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"],
"0.375": ["-math.cos(q.anim_time*90*16/3*2)*-12.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"],
"0.5": ["-math.cos(q.anim_time*90*16/3*2)*-9.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"],
"0.5417": ["-math.cos(q.anim_time*90*16/3*2)*-12.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"]
},
"position": {
"0.0": [0, "math.cos(q.anim_time*90*16/3*2)*0.2", 0],
"0.0417": [0, -0.075, 0.025],
"0.0833": [0, "-0.2+math.cos(q.anim_time*90*16/3*2)*0.2", -0.2],
"0.125": [0, -0.22, -0.17],
"0.1667": [0, -0.21, -0.185],
"0.375": [0, "math.cos(q.anim_time*90*16/3*2)*0.2", 0],
"0.4167": [0, -0.075, 0.025],
"0.4583": [0, "-0.2+math.cos(q.anim_time*90*16/3*2)*0.2", -0.2],
"0.5": [0, -0.22, -0.17],
"0.5417": [0, -0.21, -0.185],
"0.75": [0, "math.cos(q.anim_time*90*16/3*2)*0.2", 0]
}
},
"head": {
"rotation": ["5-math.cos(q.anim_time*90*16/3*2)*18", 0, 0],
"position": [0, 0, "-0.2+math.cos(q.anim_time*90*16/3*2)*0.3"]
},
"scruff_left": {
"rotation": [0, "-0-math.cos(q.anim_time*90*16/3*2-30)*7", 0]
},
"scruff_right": {
"rotation": [0, "0+math.cos(q.anim_time*90*16/3*2-30)*7", 0]
},
"scruff_top_right": {
"rotation": ["-0-math.cos(q.anim_time*90*16/3*2-30)*5", 0, 0]
},
"scruff_bottom": {
"rotation": ["-0-math.cos(q.anim_time*90*16/3*2)*-12", 0, 0]
},
"leg_left": {
"rotation": {
"0.0": [25, -17.5, 0],
"0.125": {
"pre": [21.25, -12.5, 0],
"post": [21.25, -12.5, 0],
"lerp_mode": "catmullrom"
},
"0.375": [-40, -5, 0],
"0.75": [25, -17.5, 0]
},
"position": {
"0.0": [0.3, 0.1, 0.6],
"0.0417": [0.23, 0.25, 0.71],
"0.125": {
"pre": [0, 0.68, -0.08],
"post": [0, 0.68, -0.08],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [0, 0.97, -1.41],
"lerp_mode": "catmullrom"
},
"0.375": [0, -0.5, -2.15],
"0.5": [0, -0.19, -1.02],
"0.5833": [0, -0.07, -0.3],
"0.625": [0, -0.02, 0.21],
"0.7083": [0.1, 0.215, 0.43],
"0.75": [0.3, 0.1, 0.6]
}
},
"leg_left2": {
"rotation": {
"0.0": [0, 0, 0],
"0.125": {
"pre": [-30.83, 0, 0],
"post": [-30.83, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [-2.65, 0, 0],
"lerp_mode": "catmullrom"
},
"0.375": [12.5, 0, 0],
"0.75": [0, 0, 0]
}
},
"foot_left": {
"rotation": {
"0.0": [27.5, 0, -5],
"0.125": {
"pre": [20.17, 0, 0],
"post": [20.17, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [12.596, 0.6637, -1.3486],
"lerp_mode": "catmullrom"
},
"0.375": [28, 0, 0],
"0.625": [-7.33, 0, 0],
"0.75": [27.5, 0, -5]
},
"position": {
"0.0": [0, 0.25, 0],
"0.125": [0, 0, 0],
"0.375": [0, 0, 0],
"0.625": [0, 0.17, 0],
"0.75": [0, 0.25, 0]
}
},
"toe_back_left": {
"rotation": {
"0.0": [-20, 0, 0],
"0.2083": {
"pre": [-8.33, 0, 0],
"post": [-8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [-20, 0, 0]
}
},
"toe_inner_left": {
"rotation": {
"0.0": [-55, 10, -7.5],
"0.125": {
"pre": [7.5, 15, 0],
"post": [7.5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, 15, 0],
"0.625": [0, 15, 0],
"0.75": [-55, 10, -7.5]
},
"position": {
"0.0": [0, -0.75, 1.35],
"0.125": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [0, -0.75, 1.35]
}
},
"toe_outer_left": {
"rotation": {
"0.0": [-51.5, -2.5, 10],
"0.125": {
"pre": [5, -15, 0],
"post": [5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, -15, 0],
"0.625": [0, -15, 0],
"0.75": [-51.5, -2.5, 10]
},
"position": {
"0.0": [0, -0.75, 1.35],
"0.125": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [0, -0.75, 1.35]
}
},
"leg_right": {
"rotation": {
"0.0": [-40, 5, 0],
"0.375": [25, 17.5, 0],
"0.5": {
"pre": [21.25, 12.5, 0],
"post": [21.25, 12.5, 0],
"lerp_mode": "catmullrom"
},
"0.75": [-40, 5, 0]
},
"position": {
"0.0": [0, -0.5, -2.15],
"0.125": [0, -0.19, -1.02],
"0.2083": [0, -0.07, -0.3],
"0.25": [0, -0.02, 0.21],
"0.3333": [-0.1, 0.215, 0.43],
"0.375": [-0.3, 0.1, 0.6],
"0.4167": [-0.23, 0.25, 0.71],
"0.5": {
"pre": [0, 0.68, -0.08],
"post": [0, 0.68, -0.08],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [0, 0.97, -1.41],
"lerp_mode": "catmullrom"
},
"0.75": [0, -0.5, -2.15]
}
},
"leg_right2": {
"rotation": {
"0.0": [12.5, 0, 0],
"0.375": [0, 0, 0],
"0.5": {
"pre": [-30.83, 0, 0],
"post": [-30.83, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [-2.65, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": [12.5, 0, 0]
}
},
"foot_right": {
"rotation": {
"0.0": [28, 0, 0],
"0.25": [-7.33, 0, 0],
"0.375": [27.5, 0, 5],
"0.5": {
"pre": [20.17, 0, 0],
"post": [20.17, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [12.596, -0.6637, 1.3486],
"lerp_mode": "catmullrom"
},
"0.75": [28, 0, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 0.17, 0],
"0.375": [0, 0.25, 0],
"0.5": [0, 0, 0],
"0.75": [0, 0, 0]
}
},
"toe_back_right": {
"rotation": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [-20, 0, 0],
"0.5833": {
"pre": [-8.33, 0, 0],
"post": [-8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, 0, 0]
}
},
"toe_inner_right": {
"rotation": {
"0.0": [0, -15, 0],
"0.25": [0, -15, 0],
"0.375": [-55, -10, 7.5],
"0.5": {
"pre": [7.5, -15, 0],
"post": [7.5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, -15, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [0, -0.75, 1.35],
"0.5": [0, 0, 0],
"0.75": [0, 0, 0]
}
},
"toe_outer_right": {
"rotation": {
"0.0": [0, 15, 0],
"0.25": [0, 15, 0],
"0.375": [-51.5, 2.5, -10],
"0.5": {
"pre": [5, 15, 0],
"post": [5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, 15, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [0, -0.75, 1.35],
"0.5": [0, 0, 0],
"0.75": [0, 0, 0]
}
}
}
},
"animation.spearow.air_idle": {
"loop": true,
"animation_length": 4,
"bones": {
"body": {
"position": ["-math.sin(q.anim_time*90*3-45)*0.5", "math.sin(q.anim_time*90*6-45)*1.5", "math.sin(q.anim_time*90*6-45)*1/2"]
},
"torso": {
"rotation": ["-math.sin(q.anim_time*90*6-120)*-4", 0, "math.sin(q.anim_time*90*3-90)*4"]
},
"wing_open_left": {
"rotation": ["-math.sin(q.anim_time*90*6-90)*6", 0, "math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_open_left2": {
"rotation": [0, 0, "20+math.sin(q.anim_time*90*6-45)*30"]
},
"wing_open_left3": {
"rotation": [0, 0, "0+math.sin(q.anim_time*90*6-75)*30"]
},
"wing_open_right": {
"rotation": ["-math.sin(q.anim_time*90*6-90)*6", 0, "-math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_open_right2": {
"rotation": [0, 0, "-20-math.sin(q.anim_time*90*6-45)*30"]
},
"wing_open_right3": {
"rotation": [0, 0, "-0-math.sin(q.anim_time*90*6-75)*30"]
},
"tail2": {
"rotation": ["-math.sin(q.anim_time*90*6-30)*5", 0, 0]
},
"tail3": {
"rotation": ["-math.sin(q.anim_time*90*6-90)*5", 0, 0]
},
"neck": {
"rotation": ["5-math.sin(q.anim_time*90*6-140)*-1", 0, "math.sin(q.anim_time*90*3-00)*-1"]
},
"head": {
"rotation": ["-math.sin(q.anim_time*90*6-110)*3", 0, "math.sin(q.anim_time*90*3-120)*-2"]
},
"scruff_left": {
"rotation": [0, "-0-math.sin(q.anim_time*90*6-170)*4", 0]
},
"scruff_right": {
"rotation": [0, "0+math.sin(q.anim_time*90*6-170)*4", 0]
},
"scruff_top_right": {
"rotation": ["-0-math.sin(q.anim_time*90*6-210)*6", 0, 0]
},
"scruff_bottom": {
"rotation": ["-0-math.sin(q.anim_time*90*6-170)*3", 0, 0]
},
"leg_left": {
"rotation": [50, 0, 0]
},
"leg_left2": {
"rotation": [-100, 0, 0]
},
"foot_left": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", -10, "math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_left": {
"rotation": [-27.5, 0, 0]
},
"toe_inner_left": {
"rotation": [37.5, 0, 0]
},
"toe_outer_left": {
"rotation": [37.5, 0, 0]
},
"leg_right": {
"rotation": [50, 0, 0]
},
"leg_right2": {
"rotation": [-100, 0, 0]
},
"foot_right": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", 10, "-math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_right": {
"rotation": [-27.5, 0, 0]
},
"toe_inner_right": {
"rotation": [37.5, 0, 0]
},
"toe_outer_right": {
"rotation": [37.5, 0, 0]
}
},
"timeline": {
"0.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"1.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"3.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"4.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};"
}
},
"animation.spearow.air_fly": {
"loop": true,
"animation_length": 4,
"bones": {
"body": {
"position": ["-math.sin(q.anim_time*90*3-45)*0.5", "math.sin(q.anim_time*90*6-45)*1.5", "math.sin(q.anim_time*90*6-45)*1/2"]
},
"torso": {
"rotation": ["15-math.sin(q.anim_time*90*6-120)*-1.5", 0, "math.sin(q.anim_time*90*3-90)*4"]
},
"wing_open_left": {
"rotation": ["-math.sin(q.anim_time*90*6-90)*6", 0, "math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_open_left2": {
"rotation": [0, 0, "20+math.sin(q.anim_time*90*6-45)*30"]
},
"wing_open_left3": {
"rotation": [0, 0, "0+math.sin(q.anim_time*90*6-75)*30"]
},
"wing_open_right": {
"rotation": ["-math.sin(q.anim_time*90*6-90)*6", 0, "-math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_open_right2": {
"rotation": [0, 0, "-20-math.sin(q.anim_time*90*6-45)*30"]
},
"wing_open_right3": {
"rotation": [0, 0, "-0-math.sin(q.anim_time*90*6-75)*30"]
},
"tail": {
"rotation": [-10, 0, 0]
},
"tail2": {
"rotation": ["-math.sin(q.anim_time*90*6-30)*5", 0, 0]
},
"tail3": {
"rotation": ["-math.sin(q.anim_time*90*6-90)*5", 0, 0]
},
"neck": {
"rotation": ["5-math.sin(q.anim_time*90*6-140)*-1", 0, "math.sin(q.anim_time*90*3-00)*-1"]
},
"head_ai": {
"rotation": [5, 0, 0]
},
"head": {
"rotation": ["-7.5-math.sin(q.anim_time*90*6-110)*3", 0, "math.sin(q.anim_time*90*3-120)*-2"]
},
"scruff_left": {
"rotation": [0, "-0-math.sin(q.anim_time*90*6-170)*4", 0]
},
"scruff_right": {
"rotation": [0, "0+math.sin(q.anim_time*90*6-170)*4", 0]
},
"scruff_top_right": {
"rotation": ["-0-math.sin(q.anim_time*90*6-210)*6", 0, 0]
},
"scruff_bottom": {
"rotation": ["-0-math.sin(q.anim_time*90*6-170)*3", 0, 0]
},
"leg_left": {
"rotation": [50, 0, 0]
},
"leg_left2": {
"rotation": [-100, 0, 0]
},
"foot_left": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", -10, "math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_left": {
"rotation": [-27.5, 0, 0]
},
"toe_inner_left": {
"rotation": [37.5, 0, 0]
},
"toe_outer_left": {
"rotation": [37.5, 0, 0]
},
"leg_right": {
"rotation": [50, 0, 0]
},
"leg_right2": {
"rotation": [-100, 0, 0]
},
"foot_right": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", 10, "-math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_right": {
"rotation": [-27.5, 0, 0]
},
"toe_inner_right": {
"rotation": [37.5, 0, 0]
},
"toe_outer_right": {
"rotation": [37.5, 0, 0]
}
},
"timeline": {
"0.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"1.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"3.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"4.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};"
}
}
}
}
@@ -2,6 +2,312 @@
"format_version": "1.8.0",
"animations": {
"animation.ninetales.ground_idle": {
"loop": true,
"bones": {
"body": {
"position": [0, -0.3, 0]
},
"torso": {
"rotation": ["5+math.sin(q.anim_time*90*1-0)*0.5", 0, "0-math.sin(q.anim_time*90*0.5-30)*0.5"]
},
"chest": {
"rotation": ["-4.8+math.sin(q.anim_time*90*1-30)*0.5", 0, -1.2]
},
"neck": {
"rotation": ["-12.5+math.sin(q.anim_time*90*1-60)*1", 0, 0]
},
"neck_fur_left2": {
"rotation": [0, 0, "-7.5-math.sin(q.anim_time*90*1-90)*1"]
},
"neck_fur_right2": {
"rotation": [0, 0, "7.5+math.sin(q.anim_time*90*1-90)*1"]
},
"neck2": {
"rotation": ["-12.5+math.sin(q.anim_time*90*1-90)*0.5", 0, 0]
},
"head_rotation": {
"rotation": ["32.5+math.sin(q.anim_time*90*1-90)*-2", 0, "0-math.sin(q.anim_time*90*1-120)*-0.5"]
},
"hair2": {
"rotation": ["-2.5+math.sin(q.anim_time*90*1-210)*1", 0, 0]
},
"hair_back_top": {
"rotation": ["-2.5+math.sin(q.anim_time*90*1-300)*2", 0, 0]
},
"hair_back_top2": {
"rotation": ["-22.5+math.sin(q.anim_time*90*1-360)*4", 0, 0]
},
"hair_back_bottom2": {
"rotation": ["-20+math.sin(q.anim_time*90*1-380)*4", 0, 0]
},
"tail_middle": {
"rotation": ["42.5+math.sin(q.anim_time*90*1-60)*-0.5", 0, 0]
},
"tail_middle2": {
"rotation": ["7.5+math.sin(q.anim_time*90*1-120)*-1", 0, 0]
},
"tail_middle3": {
"rotation": ["17.5+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_middle4": {
"rotation": ["-37.5+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_middle5": {
"rotation": ["-35+math.sin(q.anim_time*90*1-300)*-6", 2.5, 0]
},
"tail_middle6": {
"rotation": ["-42.5+math.sin(q.anim_time*90*1-300)*-6", 0, 0]
},
"tail_middle7": {
"rotation": ["-25+math.sin(q.anim_time*90*1-400)*-6", 0, 0]
},
"tail_middle8": {
"rotation": ["35+math.sin(q.anim_time*90*1-460)*-12", 0, 0]
},
"tail_left1_rotation": {
"rotation": ["27.525638448+math.sin(q.anim_time*90*1-60)*-0.5", 2.04767, 1.43455]
},
"tail_left1_2": {
"rotation": ["15.013645339+math.sin(q.anim_time*90*1-120)*-1", 2.41476, 0.64743]
},
"tail_left1_3": {
"rotation": ["17.5+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_left1_4": {
"rotation": ["-32.599068173+math.sin(q.anim_time*90*1-240)*-4", -4.21541, 2.69136]
},
"tail_left1_5": {
"rotation": ["-37.605552+math.sin(q.anim_time*90*1-300)*-6", -3.9649, 3.04868]
},
"tail_left1_6": {
"rotation": ["-40.107597848+math.sin(q.anim_time*90*1-360)*-4", 3.82821, -3.21873]
},
"tail_left1_7": {
"rotation": ["-27.702122918+math.sin(q.anim_time*90*1-400)*-6", 6.64851, -3.47875]
},
"tail_left1_8": {
"rotation": ["30.867477791+math.sin(q.anim_time*90*1-460)*-12", 12.95254, 7.63074]
},
"tail_left2_rotation": {
"rotation": ["22.577335588+math.sin(q.anim_time*90*1-60)*-0.5", 4.61854, 1.91757]
},
"tail_left2_2": {
"rotation": ["12.5+math.sin(q.anim_time*90*1-120)*-1", 0, 0]
},
"tail_left2_3": {
"rotation": ["15+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_left2_4": {
"rotation": ["-20+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_left2_5": {
"rotation": ["-56.265008831+math.sin(q.anim_time*90*1-300)*-6", -9.93191, 14.48174]
},
"tail_left2_6": {
"rotation": ["-37.158458405+math.sin(q.anim_time*90*1-300)*-6", 18.26883, -13.36473]
},
"tail_left2_7": {
"rotation": ["33.125975871+math.sin(q.anim_time*90*1-360)*-12", 10.51794, 6.79286]
},
"tail_left3_rotation": {
"rotation": ["10+math.sin(q.anim_time*90*1-60)*-0.5", 0, 0]
},
"tail_left3_2": {
"rotation": ["22.577335588+math.sin(q.anim_time*90*1-120)*-1", 4.61854, 1.91757]
},
"tail_left3_3": {
"rotation": ["10+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_left3_4": {
"rotation": ["-30+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_left3_5": {
"rotation": ["-27.860824367+math.sin(q.anim_time*90*1-300)*-6", -8.86042, 4.65468]
},
"tail_left3_6": {
"rotation": ["-43.1852829+math.sin(q.anim_time*90*1-300)*-6", 9.18229, -8.51814]
},
"tail_left3_7": {
"rotation": ["35.413183453+math.sin(q.anim_time*90*1-360)*-12", 8.17773, 5.77509]
},
"tail_right1_rotation": {
"rotation": ["27.525638448+math.sin(q.anim_time*90*1-60)*-0.5", -2.04767, -1.43455]
},
"tail_right1_2": {
"rotation": ["15.013645339+math.sin(q.anim_time*90*1-120)*-1", -2.41476, -0.64743]
},
"tail_right1_3": {
"rotation": ["17.5+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_right1_4": {
"rotation": ["-32.599068173+math.sin(q.anim_time*90*1-240)*-4", 4.21541, -2.69136]
},
"tail_right1_5": {
"rotation": ["-37.605552+math.sin(q.anim_time*90*1-300)*-6", 3.9649, -3.04868]
},
"tail_right1_6": {
"rotation": ["-40.107597848+math.sin(q.anim_time*90*1-360)*-4", -3.82821, 3.21873]
},
"tail_right1_7": {
"rotation": ["-27.702122918+math.sin(q.anim_time*90*1-400)*-6", -6.64851, 3.47875]
},
"tail_right1_8": {
"rotation": ["30.867477791+math.sin(q.anim_time*90*1-460)*-12", -12.95254, -7.63074]
},
"tail_right2_rotation": {
"rotation": ["22.577335588+math.sin(q.anim_time*90*1-60)*-0.5", -4.61854, -1.91757]
},
"tail_right2_2": {
"rotation": ["12.5+math.sin(q.anim_time*90*1-120)*-1", 0, 0]
},
"tail_right2_3": {
"rotation": ["15+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_right2_4": {
"rotation": ["-20+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_right2_5": {
"rotation": ["-56.265008831+math.sin(q.anim_time*90*1-300)*-6", 9.93191, -14.48174]
},
"tail_right2_6": {
"rotation": ["-37.158458405+math.sin(q.anim_time*90*1-300)*-6", -18.26883, 13.36473]
},
"tail_right2_7": {
"rotation": ["33.125975871+math.sin(q.anim_time*90*1-360)*-12", -10.51794, -6.79286]
},
"leg_front_left": {
"rotation": ["-9.981069394+math.sin(q.anim_time*90*1-0)*-1", 0.43523, 4.98107],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.025", "0-math.sin(q.anim_time*90*1-0)*-0.15"]
},
"leg_front_left2": {
"rotation": ["-2.5+math.sin(q.anim_time*90*1-0)*0.5", 0, 0]
},
"foot_front_left": {
"rotation": ["12.5+math.sin(q.anim_time*90*1-0)*0.5", 0, -5]
},
"leg_back_left": {
"rotation": ["10+math.sin(q.anim_time*90*1-0)*1", 0, -7.5],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.2", "0-math.sin(q.anim_time*90*1-0)*0.25"]
},
"leg_back_left2": {
"rotation": ["-2.5+math.sin(q.anim_time*90*1-0)*1", 0, 0]
},
"leg_back_left3": {
"rotation": ["-2.5+math.sin(q.anim_time*90*1-0)*-1", 0, 0]
},
"foot_back_left": {
"rotation": ["-5+math.sin(q.anim_time*90*1-0)*-1", 0, 7.5]
},
"leg_front_right": {
"rotation": ["10.018930606+math.sin(q.anim_time*90*1-0)*-1", -0.43523, -4.98107],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.025", "0-math.sin(q.anim_time*90*1-0)*-0.15"]
},
"leg_front_right2": {
"rotation": ["-10+math.sin(q.anim_time*90*1-0)*0.5", 0, 0]
},
"foot_front_right": {
"rotation": ["-0+math.sin(q.anim_time*90*1-0)*0.5", 0, 5]
},
"leg_back_right": {
"rotation": ["17.521361057+math.sin(q.anim_time*90*1-0)*1", 0.32621, 7.49294],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.2", "0-math.sin(q.anim_time*90*1-0)*0.25"]
},
"leg_back_right2": {
"rotation": ["-5+math.sin(q.anim_time*90*1-0)*1", 0, 0]
},
"leg_back_right3": {
"rotation": ["2.5+math.sin(q.anim_time*90*1-0)*-1", 0, 0]
},
"foot_back_right": {
"rotation": ["-15+math.sin(q.anim_time*90*1-0)*-1", 0, -7.5]
},
"tail_right3_rotation": {
"rotation": ["10+math.sin(q.anim_time*90*1-60)*-0.5", 0, 0]
},
"tail_right3_2": {
"rotation": ["22.577335588+math.sin(q.anim_time*90*1-120)*-1", -4.61854, -1.91757]
},
"tail_right3_3": {
"rotation": ["10+math.sin(q.anim_time*90*1-180)*-1", 0, 0]
},
"tail_right3_4": {
"rotation": ["-30+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_right3_5": {
"rotation": ["-27.860824367+math.sin(q.anim_time*90*1-300)*-6", 8.86042, -4.65468]
},
"tail_right3_6": {
"rotation": ["-43.1852829+math.sin(q.anim_time*90*1-300)*-6", -9.18229, 8.51814]
},
"tail_right3_7": {
"rotation": ["35.413183453+math.sin(q.anim_time*90*1-360)*-12", -8.17773, -5.77509]
},
"tail_left4_rotation": {
"rotation": ["-0+math.sin(q.anim_time*90*1-60)*-0.5", 10, 0]
},
"tail_left4_2": {
"rotation": ["17.5+math.sin(q.anim_time*90*1-120)*-1", 0, 0]
},
"tail_left4_3": {
"rotation": ["20.4458201+math.sin(q.anim_time*90*1-180)*-2", -11.73507, -4.3361]
},
"tail_left4_4": {
"rotation": ["-41.341977449+math.sin(q.anim_time*90*1-240)*-4", -13.31791, 11.45696]
},
"tail_left4_5": {
"rotation": ["-35.413183453+math.sin(q.anim_time*90*1-300)*-6", 8.17773, -5.77509]
},
"tail_left4_6": {
"rotation": ["39.711284319+math.sin(q.anim_time*90*1-360)*-12", 17.67415, 14.15251]
},
"tail_right4_rotation": {
"rotation": ["-0+math.sin(q.anim_time*90*1-60)*-0.5", -10, 0]
},
"tail_right4_2": {
"rotation": ["17.5+math.sin(q.anim_time*90*1-120)*-1", 0, 0]
},
"tail_right4_3": {
"rotation": ["20.4458201+math.sin(q.anim_time*90*1-180)*-2", 11.73507, 4.3361]
},
"tail_right4_4": {
"rotation": ["-41.341977449+math.sin(q.anim_time*90*1-240)*-4", 13.31791, -11.45696]
},
"tail_right4_5": {
"rotation": ["-35.413183453+math.sin(q.anim_time*90*1-300)*-6", -8.17773, 5.77509]
},
"tail_right4_6": {
"rotation": ["39.711284319+math.sin(q.anim_time*90*1-360)*-12", -17.67415, -14.15251]
},
"neck_fur_front": {
"rotation": ["-0+math.sin(q.anim_time*90*1-60)*-1", 0, 0]
},
"neck_fur_front2": {
"rotation": ["-0+math.sin(q.anim_time*90*1-90)*2", 0, 0]
},
"neck_fur_front3": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*2", 0, 0]
},
"ear_left": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*1", 0, "0-math.sin(q.anim_time*90*1-30)*2"]
},
"ear_right": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*1", 0, "-0+math.sin(q.anim_time*90*1-30)*2"]
},
"hair": {
"rotation": ["-0+math.sin(q.anim_time*90*1-150)*2", 0, 0]
},
"hair_detail": {
"rotation": ["-0+math.sin(q.anim_time*90*1-180)*1", 0, 0]
},
"hair_detail2": {
"rotation": ["-0+math.sin(q.anim_time*90*1-240)*1", 0, 0]
},
"hair_back_bottom": {
"rotation": ["-0+math.sin(q.anim_time*90*1-320)*2", 0, 0]
}
}
},
"animation.ninetales.base": {
"loop": true,
"bones": {
"body": {
@@ -276,64 +582,6 @@
}
}
},
"animation.ninetales.cry": {
"animation_length": 1,
"bones": {
"mouth": {
"position": {
"0.0": [0, 0, 0],
"0.25": [0, -0.0999, -0.00436],
"0.75": [0, -0.0999, -0.00436],
"1.0": [0, 0, 0]
}
},
"head": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [-15, 0, 0],
"lerp_mode": "catmullrom"
},
"0.6667": {
"post": [-15, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"jaw": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [65, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": {
"post": [65, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
}
},
"sound_effects": {
"0.0": {
"effect": "pokemon.ninetales.cry"
}
}
},
"animation.ninetales.blink": {
"animation_length": 0.1667,
"bones": {
@@ -413,6 +661,164 @@
"position": [-0.1, 0, -0.1]
}
}
},
"animation.ninetales.cry": {
"animation_length": 2,
"bones": {
"torso": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [1, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [-2, 0, 0],
"lerp_mode": "catmullrom"
},
"1.3333": {
"post": [-4, 0, 0],
"lerp_mode": "catmullrom"
},
"1.6667": {
"post": [-0.5, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"neck2": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2083": {
"post": [2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4167": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"head_rotation": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2083": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4583": {
"post": [-22.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-27, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {"pre": [0, 0, 0], "post": [0.1, 0, -0.1]},
"1.5": {"pre": [0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {"pre": [0, 0, 0], "post": [-0.1, 0, -0.1]},
"1.5": {"pre": [-0.1, 0, -0.1], "post": [0, 0, 0]}
}
},
"jaw": {
"rotation": {
"0.2917": {
"pre": [0, 0, 0],
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [47.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [20, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
},
"position": [0, 0, 0]
},
"hair": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.1667": {
"post": [10, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.1667": {
"post": [-2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
}
},
"sound_effects": {
"0.3333": {
"effect": "pokemon.ninetales.cry"
}
}
}
}
}
@@ -1,7 +1,7 @@
{
"format_version": "1.8.0",
"animations": {
"animation.ninetales_alolan.ground_idle": {
"animation.ninetales_alolan.base": {
"loop": true,
"bones": {
"body": {
@@ -381,61 +381,411 @@
}
}
},
"animation.ninetales_alolan.cry": {
"animation_length": 1,
"animation.ninetales_alolan.ground_idle": {
"loop": true,
"bones": {
"mouth": {
"position": {
"0.0": [0, 0, 0],
"0.25": [0, -0.0999, -0.00436],
"0.75": [0, -0.0999, -0.00436],
"1.0": [0, 0, 0]
}
"body": {
"position": [0, -0.25, 0]
},
"head": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [-15, 0, 0],
"lerp_mode": "catmullrom"
},
"0.6667": {
"post": [-15, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
"torso": {
"rotation": ["2.5+math.sin(q.anim_time*90*1-0)*0.5", 0, "0-math.sin(q.anim_time*90*0.5-30)*0.5"]
},
"jaw": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [65, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": {
"post": [65, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
}
},
"sound_effects": {
"0.0": {
"effect": "pokemon.ninetales_alolan.cry"
"chest": {
"rotation": ["-5+math.sin(q.anim_time*90*1-30)*0.5", 0, 0]
},
"neck": {
"rotation": ["-10+math.sin(q.anim_time*90*1-60)*1", 0, 0]
},
"neck2": {
"rotation": ["-10+math.sin(q.anim_time*90*1-90)*0.5", 0, 0]
},
"head_rotation": {
"rotation": ["14.54701+math.sin(q.anim_time*90*1-90)*-2", -3.59452, "0.40543-math.sin(q.anim_time*90*1-120)*-0.5"]
},
"hair": {
"rotation": ["13.75153+math.sin(q.anim_time*90*1-90)*1", 7.26228, 1.47729]
},
"hair2": {
"rotation": ["-12.54624+math.sin(q.anim_time*90*1-120)*2", 4.88119, -1.08482]
},
"hair3": {
"rotation": ["-24.58726+math.sin(q.anim_time*90*1-180)*2", -15.69466, 1.78427]
},
"hair4": {
"rotation": ["3.48303+math.sin(q.anim_time*90*1-240)*4", -24.77164, -8.26511]
},
"hair5": {
"rotation": ["-7.97337+math.sin(q.anim_time*90*1-300)*8", -59.03741, 4.53051]
},
"hair_bottom": {
"rotation": ["7.5+math.sin(q.anim_time*90*1-360)*12", 0, 0]
},
"hair_bottom2": {
"rotation": ["-27.70212+math.sin(q.anim_time*90*1-420)*18", -6.64851, 3.47875]
},
"hair_bottom3": {
"rotation": ["25.56145+math.sin(q.anim_time*90*1-480)*24", -27.51235, -12.45891]
},
"hair6": {
"rotation": ["7.5+math.sin(q.anim_time*90*1-360)*12", 0, 0]
},
"hair7": {
"rotation": ["-40.98084+math.sin(q.anim_time*90*1-420)*18", -11.43563, 9.77243]
},
"hair8": {
"rotation": ["30.86748+math.sin(q.anim_time*90*1-480)*24", -12.95254, -7.63074]
},
"tail": {
"rotation": [0, 0, 0]
},
"tail_middle_rotation": {
"rotation": ["65+math.sin(q.anim_time*90*1-60)*-0.5", 0, 0]
},
"tail_middle2": {
"rotation": ["-7.5+math.sin(q.anim_time*90*1-120)*-1", 0, 0]
},
"tail_middle3": {
"rotation": ["-25+math.sin(q.anim_time*90*1-180)*-2", 0, 0]
},
"tail_middle4": {
"rotation": ["-30+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_middle5": {
"rotation": ["-25+math.sin(q.anim_time*90*1-300)*-6", 0, 0]
},
"tail_middle6": {
"rotation": ["-12.62353+math.sin(q.anim_time*90*1-360)*-12", 7.24305, -1.95155]
},
"tail_middle7": {
"rotation": ["-31.18939+math.sin(q.anim_time*90*1-400)*-12", -15.09492, 8.95891]
},
"tail_middle8": {
"rotation": ["15.22074+math.sin(q.anim_time*90*1-460)*-12", 9.65594, 2.61298]
},
"tail_middle9": {
"rotation": ["-20+math.sin(q.anim_time*90*1-520)*-18", 0, 0]
},
"tail_left1_rotation": {
"rotation": ["64.2174+math.sin(q.anim_time*90*1-60)*-0.5", 8.98581, 12.04686]
},
"tail_left1_2": {
"rotation": ["-17.51565+math.sin(q.anim_time*90*1-120)*-1", -2.38422, 0.7522]
},
"tail_left1_3": {
"rotation": ["-10.03742+math.sin(q.anim_time*90*1-180)*-2", -4.92385, 0.87038]
},
"tail_left1_4": {
"rotation": ["-37.52635+math.sin(q.anim_time*90*1-240)*-4", -1.98315, 1.52251]
},
"tail_left1_5": {
"rotation": ["-17.51565+math.sin(q.anim_time*90*1-300)*-6", -2.38422, 0.7522]
},
"tail_left1_6": {
"rotation": ["-25.08377+math.sin(q.anim_time*90*1-360)*-12", -4.53051, 2.11751]
},
"tail_left1_7": {
"rotation": ["-15.22074+math.sin(q.anim_time*90*1-400)*-12", 9.65594, -2.61298]
},
"tail_left1_8": {
"rotation": ["30.71983+math.sin(q.anim_time*90*1-460)*-12", -13.97399, -3.85625]
},
"tail_left1_9": {
"rotation": ["-42.24682+math.sin(q.anim_time*90*1-520)*-18", 17.04679, -14.9092]
},
"tail_left2_rotation": {
"rotation": ["63.58314+math.sin(q.anim_time*90*1-60)*-0.5", 11.98346, 41.65117]
},
"tail_left2_2": {
"rotation": ["-15.50409+math.sin(q.anim_time*90*1-120)*-1", -14.47751, 3.96713]
},
"tail_left2_3": {
"rotation": ["-9.9045+math.sin(q.anim_time*90*1-180)*-2", -2.61747, -1.08202]
},
"tail_left2_4": {
"rotation": ["-27.5209+math.sin(q.anim_time*90*1-240)*-4", -2.26564, 1.0571]
},
"tail_left2_5": {
"rotation": ["-30.09468+math.sin(q.anim_time*90*1-300)*-6", -4.32875, 2.50477]
},
"tail_left2_6": {
"rotation": ["-13.06681+math.sin(q.anim_time*90*1-360)*-12", 11.06861, -5.84485]
},
"tail_left2_7": {
"rotation": ["-25.08377+math.sin(q.anim_time*90*1-400)*-12", 4.53051, -2.11751]
},
"tail_left2_8": {
"rotation": ["21.88023+math.sin(q.anim_time*90*1-460)*-12", -23.39896, -9.06159]
},
"tail_left2_9": {
"rotation": ["-0+math.sin(q.anim_time*90*1-520)*-18", 25, 0]
},
"tail_left3": {
"rotation": [-7.34619, 17.12827, -7.84022]
},
"tail_left3_rotation": {
"rotation": ["76.5201+math.sin(q.anim_time*90*1-60)*-0.5", -21.21599, 70.91151]
},
"tail_left3_2": {
"rotation": ["-23.00013+math.sin(q.anim_time*90*1-120)*-1", -11.40209, 5.14995]
},
"tail_left3_3": {
"rotation": ["-10+math.sin(q.anim_time*90*1-180)*-2", 0, 0]
},
"tail_left3_4": {
"rotation": ["-20+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_left3_5": {
"rotation": ["-30.02363+math.sin(q.anim_time*90*1-300)*-6", 2.16489, -1.2506]
},
"tail_left3_6": {
"rotation": ["-12.99008+math.sin(q.anim_time*90*1-360)*-12", 11.53486, -4.8493]
},
"tail_left3_7": {
"rotation": ["15+math.sin(q.anim_time*90*1-400)*-12", 20, 0]
},
"tail_left3_8": {
"rotation": ["-22.89875+math.sin(q.anim_time*90*1-460)*-12", -8.42145, 5.41207]
},
"tail_left3_9": {
"rotation": ["30+math.sin(q.anim_time*90*1-520)*-18", 22.5, 0]
},
"tail_left4_rotation": {
"rotation": ["83.07626+math.sin(q.anim_time*90*1-60)*-0.5", 7.16729, 88.89799]
},
"tail_left4_2": {
"rotation": ["-28.3693+math.sin(q.anim_time*90*1-120)*-1", -12.72029, 8.00075]
},
"tail_left4_3": {
"rotation": ["-12.60443+math.sin(q.anim_time*90*1-180)*-2", -7.32123, 1.63219]
},
"tail_left4_4": {
"rotation": ["-15.04624+math.sin(q.anim_time*90*1-240)*-4", -4.88119, 1.08482]
},
"tail_left4_5": {
"rotation": ["-29.41237+math.sin(q.anim_time*90*1-300)*-6", -2.14804, -1.84068]
},
"tail_left4_6": {
"rotation": ["-9.14867+math.sin(q.anim_time*90*1-360)*-12", 20.70481, -9.00717]
},
"tail_left4_7": {
"rotation": ["-15+math.sin(q.anim_time*90*1-400)*-12", -2.5, 0]
},
"tail_left4_8": {
"rotation": ["10+math.sin(q.anim_time*90*1-460)*-12", 20, 0]
},
"tail_left4_9": {
"rotation": ["-0+math.sin(q.anim_time*90*1-520)*-18", -25, 0]
},
"tail_right1_rotation": {
"rotation": ["64.2174+math.sin(q.anim_time*90*1-60)*-0.5", -8.98581, -12.04686]
},
"tail_right1_2": {
"rotation": ["-17.51565+math.sin(q.anim_time*90*1-120)*-1", 2.38422, -0.7522]
},
"tail_right1_3": {
"rotation": ["-10.03742+math.sin(q.anim_time*90*1-180)*-2", 4.92385, -0.87038]
},
"tail_right1_4": {
"rotation": ["-37.52635+math.sin(q.anim_time*90*1-240)*-4", 1.98315, -1.52251]
},
"tail_right1_5": {
"rotation": ["-17.51565+math.sin(q.anim_time*90*1-300)*-6", 2.38422, -0.7522]
},
"tail_right1_6": {
"rotation": ["-25.08377+math.sin(q.anim_time*90*1-360)*-12", 4.53051, -2.11751]
},
"tail_right1_7": {
"rotation": ["-15.22074+math.sin(q.anim_time*90*1-400)*-12", -9.65594, 2.61298]
},
"tail_right1_8": {
"rotation": ["30.71983+math.sin(q.anim_time*90*1-460)*-12", 13.97399, 3.85625]
},
"tail_right1_9": {
"rotation": ["-42.24682+math.sin(q.anim_time*90*1-520)*-18", -17.04679, 14.9092]
},
"tail_right2_rotation": {
"rotation": ["63.58314+math.sin(q.anim_time*90*1-60)*-0.5", -11.98346, -41.65117]
},
"tail_right2_2": {
"rotation": ["-15.50409+math.sin(q.anim_time*90*1-60)*-0.5", 14.47751, -3.96713]
},
"tail_right2_3": {
"rotation": ["-9.9045+math.sin(q.anim_time*90*1-180)*-2", 2.61747, 1.08202]
},
"tail_right2_4": {
"rotation": ["-27.5209+math.sin(q.anim_time*90*1-240)*-4", 2.26564, -1.0571]
},
"tail_right2_5": {
"rotation": ["-30.09468+math.sin(q.anim_time*90*1-300)*-6", 4.32875, -2.50477]
},
"tail_right2_6": {
"rotation": ["-13.06681+math.sin(q.anim_time*90*1-360)*-12", -11.06861, 5.84485]
},
"tail_right2_7": {
"rotation": ["-25.08377+math.sin(q.anim_time*90*1-400)*-12", -4.53051, 2.11751]
},
"tail_right2_8": {
"rotation": ["21.88023+math.sin(q.anim_time*90*1-460)*-12", 23.39896, 9.06159]
},
"tail_right2_9": {
"rotation": ["-0+math.sin(q.anim_time*90*1-520)*-18", -25, 0]
},
"leg_front_left": {
"rotation": ["-4.99527+math.sin(q.anim_time*90*1-0)*-1", 0.21782, 2.4905],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.025", "0-math.sin(q.anim_time*90*1-0)*-0.15"]
},
"leg_front_left2": {
"rotation": ["-5+math.sin(q.anim_time*90*1-0)*0.5", 0, 0]
},
"foot_front_left": {
"rotation": ["9.99067+math.sin(q.anim_time*90*1-0)*0.5", 0.43399, -2.46207]
},
"leg_back_left": {
"rotation": ["12.5048+math.sin(q.anim_time*90*1-0)*1", 1.93726, -2.22807],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.2", "0-math.sin(q.anim_time*90*1-0)*0.25"]
},
"leg_back_left2": {
"rotation": ["-5+math.sin(q.anim_time*90*1-0)*1", 0, 0]
},
"leg_back_left3": {
"rotation": ["-2.49673+math.sin(q.anim_time*90*1-0)*-1", -0.12261, 0.03929]
},
"foot_back_left": {
"rotation": ["-5.00899+math.sin(q.anim_time*90*1-0)*-1", -0.3507, 2.5603]
},
"leg_front_right": {
"rotation": ["7.47178+math.sin(q.anim_time*90*1-0)*-1", 0.65182, -4.95744],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.025", "0-math.sin(q.anim_time*90*1-0)*-0.15"]
},
"leg_front_right2": {
"rotation": ["-2.5+math.sin(q.anim_time*90*1-0)*0.5", 0, 0]
},
"foot_front_right": {
"rotation": ["-5+math.sin(q.anim_time*90*1-0)*0.5", 0, 5]
},
"leg_back_right": {
"rotation": ["-9.8369+math.sin(q.anim_time*90*1-0)*1", 5.47408, 1.12804],
"position": [0, "0-math.sin(q.anim_time*90*1-0)*0.2", "0-math.sin(q.anim_time*90*1-0)*0.25"]
},
"leg_back_right2": {
"rotation": ["5+math.sin(q.anim_time*90*1-0)*1", 0, 0]
},
"leg_back_right3": {
"rotation": ["-0+math.sin(q.anim_time*90*1-0)*-1", 0, 0]
},
"foot_back_right": {
"rotation": ["4.98247+math.sin(q.anim_time*90*1-0)*-1", 0.85481, -2.34941]
},
"tail_right3": {
"rotation": [-7.34619, -17.12827, 7.84022]
},
"tail_right3_rotation": {
"rotation": ["76.5201+math.sin(q.anim_time*90*1-60)*-0.5", 21.21599, -70.91151]
},
"tail_right3_2": {
"rotation": ["-23.00013+math.sin(q.anim_time*90*1-120)*-1", 11.40209, -5.14995]
},
"tail_right3_3": {
"rotation": ["-10+math.sin(q.anim_time*90*1-180)*-2", 0, 0]
},
"tail_right3_4": {
"rotation": ["-20+math.sin(q.anim_time*90*1-240)*-4", 0, 0]
},
"tail_right3_5": {
"rotation": ["-30.02363+math.sin(q.anim_time*90*1-300)*-6", -2.16489, 1.2506]
},
"tail_right3_6": {
"rotation": ["-12.99008+math.sin(q.anim_time*90*1-360)*-12", -11.53486, 4.8493]
},
"tail_right3_7": {
"rotation": ["15+math.sin(q.anim_time*90*1-400)*-12", -20, 0]
},
"tail_right3_8": {
"rotation": ["-22.89875+math.sin(q.anim_time*90*1-460)*-12", 8.42145, -5.41207]
},
"tail_right3_9": {
"rotation": ["30+math.sin(q.anim_time*90*1-520)*-18", -22.5, 0]
},
"tail_right4_rotation": {
"rotation": ["83.07626+math.sin(q.anim_time*90*1-60)*-0.5", -7.16729, -88.89799]
},
"tail_right4_2": {
"rotation": ["-28.3693+math.sin(q.anim_time*90*1-120)*-1", 12.72029, -8.00075]
},
"tail_right4_3": {
"rotation": ["-12.60443+math.sin(q.anim_time*90*1-180)*-2", 7.32123, -1.63219]
},
"tail_right4_4": {
"rotation": ["-15.04624+math.sin(q.anim_time*90*1-240)*-4", 4.88119, -1.08482]
},
"tail_right4_5": {
"rotation": ["-29.41237+math.sin(q.anim_time*90*1-300)*-6", 2.14804, 1.84068]
},
"tail_right4_6": {
"rotation": ["-9.14867+math.sin(q.anim_time*90*1-360)*-12", -20.70481, 9.00717]
},
"tail_right4_7": {
"rotation": ["-15+math.sin(q.anim_time*90*1-400)*-12", 2.5, 0]
},
"tail_right4_8": {
"rotation": ["10+math.sin(q.anim_time*90*1-460)*-12", -20, 0]
},
"tail_right4_9": {
"rotation": ["-0+math.sin(q.anim_time*90*1-520)*-18", 25, 0]
},
"hair_left": {
"rotation": [0, -17.5, "55+math.sin(q.anim_time*90*1-120)*2"]
},
"hair_left2": {
"rotation": [-1.6575, 12.39135, "9.82005+math.sin(q.anim_time*90*1-180)*4"]
},
"hair_left3": {
"rotation": [8.00384, 21.21629, "20.75164+math.sin(q.anim_time*90*1-240)*8"]
},
"hair_left4": {
"rotation": [7.90373, -21.34916, "-15.85201+math.sin(q.anim_time*90*1-300)*18"]
},
"hair_left5": {
"rotation": [-6.42784, -34.18333, "7.10385+math.sin(q.anim_time*90*1-360)*24"]
},
"hair_right": {
"rotation": [0, 17.5, "-55-math.sin(q.anim_time*90*1-120)*2"]
},
"hair_right2": {
"rotation": [-1.6575, -12.39135, "-9.82005-math.sin(q.anim_time*90*1-180)*4"]
},
"hair_right3": {
"rotation": [8.00384, -21.21629, "-20.75164-math.sin(q.anim_time*90*1-240)*8"]
},
"hair_right4": {
"rotation": [7.90373, 21.34916, "15.85201-math.sin(q.anim_time*90*1-300)*18"]
},
"hair_right5": {
"rotation": [-6.42784, 34.18333, "-7.10385-math.sin(q.anim_time*90*1-360)*24"]
},
"neck_fur_front": {
"rotation": ["-0+math.sin(q.anim_time*90*1-60)*-1", 0, 0]
},
"neck_fur_front2": {
"rotation": ["-0+math.sin(q.anim_time*90*1-90)*2", 0, 0]
},
"neck_fur_front3": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*2", 0, 0]
},
"neck_fur_left": {
"rotation": [0, 0, "0-math.sin(q.anim_time*90*1-90)*1"]
},
"neck_fur_right": {
"rotation": [0, 0, "-0+math.sin(q.anim_time*90*1-90)*1"]
},
"ear_left": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*1", 0, "0-math.sin(q.anim_time*90*1-30)*2"]
},
"ear_right": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*1", 0, "-0+math.sin(q.anim_time*90*1-30)*2"]
},
"hair_front": {
"rotation": ["-0+math.sin(q.anim_time*90*1-120)*2", 0, 0]
}
}
},
@@ -518,6 +868,176 @@
"position": [-0.1, 0, -0.1]
}
}
},
"animation.ninetales_alolan.cry": {
"animation_length": 2,
"bones": {
"torso": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [1, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [-2, 0, 0],
"lerp_mode": "catmullrom"
},
"1.3333": {
"post": [-4, 0, 0],
"lerp_mode": "catmullrom"
},
"1.6667": {
"post": [-0.5, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"neck2": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2083": {
"post": [2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4167": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"head_rotation": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2083": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4583": {
"post": [-22.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-27, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"1.5": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.3333": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"1.5": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"jaw": {
"rotation": {
"0.2917": {
"pre": [0, 0, 0],
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [47.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [20, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
},
"position": [0, 0, 0]
},
"hair": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.1667": {
"post": [10, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.1667": {
"post": [-2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
}
},
"sound_effects": {
"0.3333": {
"effect": "pokemon.ninetales_alolan.cry"
}
}
}
}
}
@@ -3,58 +3,274 @@
"animations": {
"animation.mudkip.ground_idle": {
"loop": true,
"animation_length": 2,
"bones": {
"head": {
"rotation": [2.5, 0, 0]
"rotation": ["2.5+math.sin(q.anim_time*90*4-90)*1.5", "math.sin(q.anim_time*90*2-60)*1.5", "math.sin(q.anim_time*90*2-60)*1"]
},
"fin2": {
"rotation": [2.5, 0, 0]
"rotation": [2.5, 0, "0+math.sin(q.anim_time*90*2-150)*-3"]
},
"cheek_left2": {
"rotation": [0, -7.5, 0]
"rotation": [0, -7.5, "0+math.sin(q.anim_time*90*4-90)*1.5"]
},
"leg_front_left": {
"rotation": [7.5, 5, 0]
"rotation": ["5+math.sin(q.anim_time*90*4)*2", 5, 0],
"position": [0, "-0.125+math.sin(q.anim_time*90*4)*-0.05", "0+math.sin(q.anim_time*90*4)*-0.07"]
},
"leg_front_left2": {
"rotation": [-7.5, 0, 0]
"rotation": ["-5+math.sin(q.anim_time*90*4)*-2", 0, 0]
},
"leg_front_right": {
"rotation": [7.5, -5, 0]
"rotation": ["5+math.sin(q.anim_time*90*4)*2", -5, 0],
"position": [0, "-0.125+math.sin(q.anim_time*90*4)*-0.05", "0+math.sin(q.anim_time*90*4)*-0.07"]
},
"leg_front_right2": {
"rotation": [-7.5, 0, 0]
"rotation": ["-5+math.sin(q.anim_time*90*4)*-2", 0, 0]
},
"leg_back_left": {
"rotation": [5, -5, 0]
"rotation": ["-5+math.sin(q.anim_time*90*4)*-1", -5, 0],
"position": [0, "-0.125+math.sin(q.anim_time*90*4)*-0.025", "0+math.sin(q.anim_time*90*4)*0.035"]
},
"leg_back_left2": {
"rotation": [-5, 0, 0]
"rotation": ["5+math.sin(q.anim_time*90*4)*1", 0, 0]
},
"leg_back_right": {
"rotation": [5, 5, 0]
"rotation": ["-5+math.sin(q.anim_time*90*4)*-1", 5, 0],
"position": [0, "-0.125+math.sin(q.anim_time*90*4)*-0.025", "0+math.sin(q.anim_time*90*4)*0.035"]
},
"leg_back_right2": {
"rotation": [-5, 0, 0]
"rotation": ["5+math.sin(q.anim_time*90*4)*1", 0, 0]
},
"body": {
"position": [0, -0.1, 0]
},
"tail": {
"rotation": [5, 0, 0]
"rotation": ["6+math.sin(q.anim_time*90*4)*1", "0+math.sin(q.anim_time*90*2-90)*-10", 0]
},
"fin": {
"rotation": [2.5, 0, 0]
"rotation": [2.5, 0, "0+math.sin(q.anim_time*90*2-90)*-4"]
},
"cheek_right2": {
"rotation": [0, -7.5, 0]
"rotation": [0, 7.5, "0+math.sin(q.anim_time*90*4-90)*-1.5"]
},
"torso": {
"rotation": ["0+math.sin(q.anim_time*90*4)*0.75", "0+math.sin(q.anim_time*90*2-30)*0.5", 0],
"position": [0, "0+math.sin(q.anim_time*90*4+30)*-0.05", 0]
},
"tail2": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-150)*-12.5", 0]
}
}
},
"animation.mudkip.battle_idle": {
"loop": true,
"animation_length": 2,
"bones": {
"head": {
"rotation": ["-7.5+math.sin(q.anim_time*90*4-90)*3", "math.sin(q.anim_time*90*2-60)*1", "math.sin(q.anim_time*90*2-60)*1"]
},
"fin2": {
"rotation": [2.5, 0, "0+math.sin(q.anim_time*90*2-120)*-3"]
},
"cheek_left2": {
"rotation": [0, -7.5, "-5+math.sin(q.anim_time*90*4-90)*1.5"]
},
"leg_front_left": {
"rotation": ["4.305+math.sin(q.anim_time*90*4)*2", 5.60922, -7.50745],
"position": [0, "-0.275+math.sin(q.anim_time*90*4)*-0.05", "0.3+math.sin(q.anim_time*90*4)*-0.07"]
},
"leg_front_left2": {
"rotation": ["-4.9575+math.sin(q.anim_time*90*4)*-2", 0.65182, 7.47178]
},
"leg_front_right": {
"rotation": ["4.305+math.sin(q.anim_time*90*4)*2", -5.60922, 7.50745],
"position": [0, "-0.275+math.sin(q.anim_time*90*4)*-0.05", "0.3+math.sin(q.anim_time*90*4)*-0.07"]
},
"leg_front_right2": {
"rotation": ["-4.9575+math.sin(q.anim_time*90*4)*-2", -0.65182, -7.47178]
},
"leg_back_left": {
"rotation": ["-5+math.sin(q.anim_time*90*4)*-1", -5, 0],
"position": [0, "-0.125+math.sin(q.anim_time*90*4)*-0.025", "0+math.sin(q.anim_time*90*4)*0.035"]
},
"leg_back_left2": {
"rotation": ["5+math.sin(q.anim_time*90*4)*1", 0, 0]
},
"leg_back_right": {
"rotation": ["-5+math.sin(q.anim_time*90*4)*-1", 5, 0],
"position": [0, "-0.125+math.sin(q.anim_time*90*4)*-0.025", "0+math.sin(q.anim_time*90*4)*0.035"]
},
"leg_back_right2": {
"rotation": ["5+math.sin(q.anim_time*90*4)*1", 0, 0]
},
"body": {
"position": [0, -0.1, 0]
},
"tail": {
"rotation": ["6+math.sin(q.anim_time*90*4)*1", "0+math.sin(q.anim_time*90*2-90)*-17.5", 0]
},
"fin": {
"rotation": [2.5, 0, "0+math.sin(q.anim_time*90*2-90)*-4"]
},
"cheek_right2": {
"rotation": [0, 7.5, "5+math.sin(q.anim_time*90*4-90)*-1.5"]
},
"torso": {
"rotation": ["10+math.sin(q.anim_time*90*4)*2.5", "math.sin(q.anim_time*90*2-30)*0.5", 0],
"position": [0, "0+math.sin(q.anim_time*90*4+30)*-0.07", 0]
},
"tail2": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-90)*-7.5", 0]
},
"eye_angry_left": {
"position": [0, 0, -0.1]
},
"eye_angry_right": {
"position": [0, 0, -0.1]
}
}
},
"animation.mudkip.cry": {
"animation_length": 1.2,
"animation_length": 1.75,
"bones": {
"torso": {
"rotation": {
"0.0": [0, 0, 0],
"0.2917": [5, 0, 0],
"0.5": [0, 0, 0],
"1.0": [-2.5, 0, 0],
"1.5": [0, 0, 0]
}
},
"head": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2083": {
"post": [10, 0, 0],
"lerp_mode": "catmullrom"
},
"0.375": {
"post": [10, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [-10, 0, 0],
"lerp_mode": "catmullrom"
},
"0.7083": {
"post": [-10, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0833": {
"post": [-17.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.2917": {
"post": [-3, 0, 0],
"lerp_mode": "catmullrom"
},
"1.625": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"jaw": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [35, 0, 0],
"lerp_mode": "catmullrom"
},
"0.7083": {
"post": [15, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0": {
"post": [60, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0833": {
"post": [60, 0, 0],
"lerp_mode": "catmullrom"
},
"1.3333": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"fin": {
"rotation": {
"0.0": [0, 0, 0],
"0.25": [6, 0, 0],
"1.0": [-5, 0, 0],
"1.5": [0, 0, 0]
}
},
"cheek_left2": {
"rotation": {
"0.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": {
"post": [0, 0, -12.5],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [0, 0, -12.5],
"lerp_mode": "catmullrom"
},
"1.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"cheek_right2": {
"rotation": {
"0.375": {
"pre": [0, 0, 0],
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": {
"post": [0, 0, 12.5],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [0, 0, 12.5],
"lerp_mode": "catmullrom"
},
"1.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"tail": {
"rotation": {
"0.0": [0, 0, 0],
"0.625": [0, "0+math.sin(q.anim_time*90*12-90)*-25", 0],
"1.625": [0, 0, 0]
}
}
},
"sound_effects": {
"0.04": {
"0.5417": {
"effect": "pokemon.mudkip.cry"
}
}
@@ -207,6 +423,14 @@
"position": [-0.1, 0, -0.1]
}
}
},
"animation.mudkip.WALKSPEED_TEST": {
"loop": true,
"bones": {
"body": {
"position": [0, 0, "q.anim_time*90*-0.07"]
}
}
}
}
}
@@ -3,83 +3,173 @@
"animations": {
"animation.marshtomp.ground_idle": {
"loop": true,
"animation_length": 4,
"bones": {
"torso": {
"rotation": [0, 0, 0]
"rotation": ["0+math.sin(q.anim_time*90*4)*-1", "math.sin(q.anim_time*90*2)*2", "math.sin(q.anim_time*90*2)*-1"],
"position": [0, "math.sin(q.anim_time*90*4-30)*-0.05", 0]
},
"chest": {
"rotation": [-2.5, 0, 0]
"rotation": ["0+math.sin(q.anim_time*90*4-30)*-1", "math.sin(q.anim_time*90*2-30)*1", "math.sin(q.anim_time*90*2-30)*-0.5"]
},
"neck": {
"rotation": ["0+math.sin(q.anim_time*90*4-30)*2.5", 0, 0]
},
"head": {
"rotation": [5, 0, 0]
"rotation": ["5+math.sin(q.anim_time*90*4-30)*-3", "math.sin(q.anim_time*90*2-30)*1", "math.sin(q.anim_time*90*2-30)*-1.5"]
},
"head_fin": {
"rotation": ["0+math.sin(q.anim_time*90*4-30)*-1.5", 0, 0]
},
"arm_left": {
"rotation": [7.5, 0, 27.5]
"rotation": [7.5, 0, "37.5+math.sin(q.anim_time*90*2)*-5"]
},
"arm_left2": {
"rotation": [0, 15, 2.5]
"rotation": [0, "15+math.sin(q.anim_time*90*2-30)*5", "2.5+math.sin(q.anim_time*90*2-60)*-5"]
},
"hand_left": {
"rotation": [0, 0, 5]
"rotation": [0, "0+math.sin(q.anim_time*90*2-120)*3", "10+math.sin(q.anim_time*90*2-150)*-2.5"]
},
"finger_front_left": {
"rotation": [0, 2.5, 15]
"rotation": [0, 0, "10+math.sin(q.anim_time*90*2-60)*-5"]
},
"finger_middle_left": {
"rotation": [0, 0, 10]
"rotation": [0, 0, "10+math.sin(q.anim_time*90*2-120)*-5"]
},
"finger_back_left": {
"rotation": [0, -5, 20]
"rotation": [0, 0, "10+math.sin(q.anim_time*90*2-180)*-5"]
},
"arm_right": {
"rotation": [7.5, 0, -27.5]
"rotation": [7.5, 0, "-37.5+math.sin(q.anim_time*90*2)*-5"]
},
"arm_right2": {
"rotation": [0, -15, -2.5]
"rotation": [0, "-15+math.sin(q.anim_time*90*2-30)*5", "-2.5+math.sin(q.anim_time*90*2-60)*-5"]
},
"hand_right": {
"rotation": [0, 0, -5]
"rotation": [0, "0+math.sin(q.anim_time*90*2-120)*3", "-10+math.sin(q.anim_time*90*2-150)*-2.5"]
},
"finger_front_right": {
"rotation": [0, -2.5, -15]
"rotation": [0, 0, "-10+math.sin(q.anim_time*90*2-60)*-5"]
},
"finger_middle_right": {
"rotation": [0, 0, -10]
"rotation": [0, 0, "-10+math.sin(q.anim_time*90*2-120)*-5"]
},
"finger_back_right": {
"rotation": [0, 5, -20]
"rotation": [0, 0, "-10+math.sin(q.anim_time*90*2-180)*-5"]
},
"tail_fin_left": {
"rotation": [-5, 45, 0]
"rotation": [-5, "45+math.sin(q.anim_time*90*2)*-12", 0]
},
"tail_fin_right": {
"rotation": [-5, -45, 0]
"rotation": [-5, "-45+math.sin(q.anim_time*90*2)*12", 0]
},
"leg_left": {
"rotation": [2.5, 0, -2.5]
"rotation": ["-2.5+math.sin(q.anim_time*90*2)*2.5", 0, 0],
"position": [0, "-0.05+math.sin(q.anim_time*90*2)*0.1", "0+math.sin(q.anim_time*90*2)*-0.1"]
},
"leg_left2": {
"rotation": ["2.5+math.sin(q.anim_time*90*2)*-2.5", 0, 0]
},
"leg_right": {
"rotation": ["-2.5+math.sin(q.anim_time*90*2)*-2.5", 0, 0],
"position": [0, "-0.05+math.sin(q.anim_time*90*2)*-0.1", "0+math.sin(q.anim_time*90*2)*0.1"]
},
"leg_right2": {
"rotation": ["2.5+math.sin(q.anim_time*90*2)*2.5", 0, 0]
}
}
},
"animation.marshtomp.battle_idle": {
"loop": true,
"animation_length": 4,
"bones": {
"torso": {
"rotation": ["0+math.sin(q.anim_time*90*4)*-2", "math.sin(q.anim_time*90*2)*4", "math.sin(q.anim_time*90*2)*-2"],
"position": [0, "math.sin(q.anim_time*90*4)*0.05", 0]
},
"chest": {
"rotation": ["-2.5+math.sin(q.anim_time*90*4-30)*-1", "math.sin(q.anim_time*90*2-30)*4", "math.sin(q.anim_time*90*2-30)*-2.5"]
},
"head": {
"rotation": ["5+math.sin(q.anim_time*90*4-30)*-3", "math.sin(q.anim_time*90*2-30)*-6", "math.sin(q.anim_time*90*2-30)*5"]
},
"arm_left": {
"rotation": [7.5, 0, "27.5+math.sin(q.anim_time*90*2)*-7.5"]
},
"arm_left2": {
"rotation": [0, "15+math.sin(q.anim_time*90*2-30)*5", "2.5+math.sin(q.anim_time*90*2-30)*-5"]
},
"hand_left": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-90)*5", "5+math.sin(q.anim_time*90*2-90)*-7.5"]
},
"finger_front_left": {
"rotation": [0, 2.5, "15+math.sin(q.anim_time*90*2-120)*-10"]
},
"finger_middle_left": {
"rotation": [0, 0, "10+math.sin(q.anim_time*90*2-150)*-10"]
},
"finger_back_left": {
"rotation": [0, -5, "20+math.sin(q.anim_time*90*2-180)*-10"]
},
"arm_right": {
"rotation": [7.5, 0, "-27.5+math.sin(q.anim_time*90*2)*-7.5"]
},
"arm_right2": {
"rotation": [0, "-15+math.sin(q.anim_time*90*2-30)*5", "-2.5+math.sin(q.anim_time*90*2-30)*-5"]
},
"hand_right": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-90)*5", "-5+math.sin(q.anim_time*90*2-90)*-7.5"]
},
"finger_front_right": {
"rotation": [0, -2.5, "-15+math.sin(q.anim_time*90*2-120)*-10"]
},
"finger_middle_right": {
"rotation": [0, 0, "-10+math.sin(q.anim_time*90*2-150)*-10"]
},
"finger_back_right": {
"rotation": [0, 5, "-20+math.sin(q.anim_time*90*2-180)*-10"]
},
"tail_fin_left": {
"rotation": [-5, "45+math.sin(q.anim_time*90*2)*-12", 0]
},
"tail_fin_right": {
"rotation": [-5, "-45+math.sin(q.anim_time*90*2)*12", 0]
},
"leg_left": {
"rotation": [2.4905, "0.2178+math.sin(q.anim_time*90*2)*1.5", -2.49527]
},
"leg_left2": {
"rotation": [5, 0, 0]
},
"foot_left": {
"rotation": [-7.5, 0, 2.5]
"rotation": [-7.47178, "0.6518+math.sin(q.anim_time*90*2)*-1.5", 2.45744]
},
"leg_right": {
"rotation": [2.5, 0, 2.5]
"rotation": [2.4905, "-0.2178+math.sin(q.anim_time*90*2)*1.5", 2.49527]
},
"leg_right2": {
"rotation": [5, 0, 0]
},
"foot_right": {
"rotation": [-7.5, 0, -2.5]
}
}
},
"animation.marshtomp.cry": {
"animation_length": 1.2,
"sound_effects": {
"0.04": {
"effect": "pokemon.marshtomp.cry"
"rotation": [-7.47178, "-0.6518+math.sin(q.anim_time*90*2)*-1.5", -2.45744]
},
"body": {
"position": [0, -0.15, 0]
},
"neck": {
"rotation": ["0+math.sin(q.anim_time*90*4-30)*2.5", 0, 0]
},
"head_fin": {
"rotation": ["0+math.sin(q.anim_time*90*4-30)*-1.5", 0, 0]
},
"eye_angry_left": {
"position": [0, 0, -0.1]
},
"eye_angry_right": {
"position": [0, 0, -0.1]
},
"jaw": {
"rotation": ["5+math.sin(q.anim_time*90*4-30)*-5", 0, 0]
}
}
},
@@ -162,6 +252,108 @@
"position": [0, 0, -0.1]
}
}
},
"animation.marshtomp.cry": {
"animation_length": 2,
"bones": {
"chest": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.125": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [-2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.7917": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0833": {
"post": [-2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5833": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"head": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.125": {
"post": [7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.4167": {
"post": [-12.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [-12.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0417": {
"post": [-12.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.4583": {
"post": [-1.53, 0, 0],
"lerp_mode": "catmullrom"
},
"1.7083": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"jaw": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2083": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5": {
"post": [27.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": {
"post": [17.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.0833": {
"post": [40, 0, 0],
"lerp_mode": "catmullrom"
},
"1.4583": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
}
},
"sound_effects": {
"0.4167": {
"effect": "pokemon.marshtomp.cry"
}
}
}
}
}
@@ -3,30 +3,33 @@
"animations": {
"animation.swampert.ground_idle": {
"loop": true,
"animation_length": 4,
"bones": {
"body": {
"position": [0, 1.75, 0]
},
"chest2": {
"rotation": [-12.5, 0, 0]
"rotation": ["-12.5+math.sin(q.anim_time*90*2-60)*0.75", 0, 0]
},
"neck": {
"rotation": [-5, 0, 0]
"rotation": ["-5+math.sin(q.anim_time*90*2-90)*1", "0+math.sin(q.anim_time*90*1-90)*-1", 0]
},
"leg_left": {
"rotation": [8.77647, -12.0131, -53.28718]
"rotation": ["8.7765+math.sin(q.anim_time*90*2)*-1.5", -12.0131, -53.28718],
"position": [0, "0+math.sin(q.anim_time*90*2)*-0.1", "0+math.sin(q.anim_time*90*2)*0.15"]
},
"leg_left2": {
"rotation": [4.54163, -0.2127, 45.09046]
"rotation": [4.5416, -0.2127, 45.09046]
},
"leg_left3": {
"rotation": [9.83802, -2.51058, 9.95791]
},
"foot_left": {
"rotation": [-27.58324, -3.55523, 20.16242]
"rotation": [-27.58324, "-3.5552+math.sin(q.anim_time*90*2)*-1.5", 20.16242]
},
"leg_right": {
"rotation": [8.77647, 12.0131, 53.2872]
"rotation": ["8.7765+math.sin(q.anim_time*90*2)*-1.5", 12.0131, 53.2872],
"position": [0, "0+math.sin(q.anim_time*90*2)*-0.1", "0+math.sin(q.anim_time*90*2)*0.15"]
},
"leg_right2": {
"rotation": [4.54163, 0.2127, -45.0905]
@@ -35,16 +38,155 @@
"rotation": [9.83802, 2.5106, -9.9579]
},
"foot_right": {
"rotation": [-27.58324, 3.5552, -20.1624]
"rotation": [-27.58324, "3.5552+math.sin(q.anim_time*90*2)*1.5", -20.1624]
},
"arm_left2": {
"rotation": [-4.36302, -31.26463, 17.62528]
"rotation": [-4.36302, -31.26463, "17.6253+math.sin(q.anim_time*90*2-90)*2"]
},
"arm_left3": {
"rotation": [1.80384, 2.38704, 35.42915]
"rotation": [1.80384, 2.38704, "35.4291+math.sin(q.anim_time*90*2-120)*2.5"]
},
"arm_left": {
"rotation": [-13.31914, -14.63639, 12.92638]
"rotation": [-13.31914, "-14.6364+math.sin(q.anim_time*90*2-60)*2", 12.92638]
},
"finger_front_left": {
"rotation": [0, "-37.5+math.sin(q.anim_time*90*2-120)*-2.5", 20]
},
"finger_middle_left": {
"rotation": [0, "-32.5+math.sin(q.anim_time*90*2-120)*-2.5", 0]
},
"finger_back_left": {
"rotation": [0, "-30+math.sin(q.anim_time*90*2-120)*-2.5", -27.5]
},
"fingers_left": {
"rotation": [0, 37.5, 0]
},
"arm_right": {
"rotation": [-13.31914, "14.6364+math.sin(q.anim_time*90*2-60)*-2", -12.9264]
},
"arm_right2": {
"rotation": [-4.33052, "31.2647+math.sin(q.anim_time*90*2-90)*2", -17.60892]
},
"arm_right3": {
"rotation": [1.80384, -2.387, "-35.4292+math.sin(q.anim_time*90*2-120)*-2.5"]
},
"fingers_right": {
"rotation": [0, -37.5, 0]
},
"finger_front_right": {
"rotation": [0, "37.5+math.sin(q.anim_time*90*2-120)*2.5", -20]
},
"finger_middle_right": {
"rotation": [0, "32.5+math.sin(q.anim_time*90*2-120)*2.5", 0]
},
"finger_back_right": {
"rotation": [0, "30+math.sin(q.anim_time*90*2-120)*2.5", 27.5]
},
"torso": {
"rotation": ["-5+math.sin(q.anim_time*90*2)*0.5", 0, 0],
"position": [0, "0+math.sin(q.anim_time*90*2+30)*-0.4", 0]
},
"chest": {
"rotation": ["-7.5+math.sin(q.anim_time*90*2-30)*1.5", 0, 0]
},
"hand_left": {
"rotation": [0, "-17.5+math.sin(q.anim_time*90*2-90)*-5", 0]
},
"finger_front_left2": {
"rotation": [0, "-12.5+math.sin(q.anim_time*90*2-150)*-5", 0]
},
"finger_middle_left2": {
"rotation": [0, "-10+math.sin(q.anim_time*90*2-150)*-5", 0]
},
"finger_back_left2": {
"rotation": [0, "-15+math.sin(q.anim_time*90*2-150)*-5", 0]
},
"hand_right": {
"rotation": [0, "17.5+math.sin(q.anim_time*90*2-90)*5", 0]
},
"finger_front_right2": {
"rotation": [0, "12.5+math.sin(q.anim_time*90*2-150)*5", 0]
},
"finger_middle_right2": {
"rotation": [0, "10+math.sin(q.anim_time*90*2-150)*5", 0]
},
"finger_back_right2": {
"rotation": [0, "15+math.sin(q.anim_time*90*2-150)*5", 0]
},
"check_left_spike_front": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*3", 0]
},
"check_left_spike_back": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*3", 0]
},
"check_right_spike_front": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*-3", 0]
},
"check_right_spike_back": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*-3", 0]
},
"eyebrow_left": {
"rotation": [0, "-2.5+math.sin(q.anim_time*90*2-60)*-2.5", 0]
},
"eyebrow_right": {
"rotation": [0, "2.5-math.sin(q.anim_time*90*2-60)*-2.5", 0]
},
"tail": {
"rotation": ["0+math.sin(q.anim_time*90*2-30)*-2.5", 0, 0]
},
"head": {
"rotation": ["12.5+math.sin(q.anim_time*90*2-90)*-2.5", "-0+math.sin(q.anim_time*90*1-90)*-0.5", 0]
}
}
},
"animation.swampert.battle_idle": {
"loop": true,
"animation_length": 4,
"bones": {
"body": {
"position": [0, 1.75, 0]
},
"chest2": {
"rotation": ["-12.5+math.sin(q.anim_time*90*2-60)*0.75", 0, 0]
},
"neck": {
"rotation": ["-5+math.sin(q.anim_time*90*2-90)*1", "0+math.sin(q.anim_time*90*1-90)*-1", 0]
},
"leg_left": {
"rotation": ["8.7765+math.sin(q.anim_time*90*2)*-1.5", -12.0131, -53.28718],
"position": [0, "0+math.sin(q.anim_time*90*2)*-0.1", "0+math.sin(q.anim_time*90*2)*0.15"]
},
"leg_left2": {
"rotation": [4.5416, -0.2127, 45.09046]
},
"leg_left3": {
"rotation": [9.83802, -2.51058, 9.95791]
},
"foot_left": {
"rotation": [-27.58324, "-3.5552+math.sin(q.anim_time*90*2)*-1.5", 20.16242]
},
"leg_right": {
"rotation": ["8.7765+math.sin(q.anim_time*90*2)*-1.5", 12.0131, 53.2872],
"position": [0, "0+math.sin(q.anim_time*90*2)*-0.1", "0+math.sin(q.anim_time*90*2)*0.15"]
},
"leg_right2": {
"rotation": [4.54163, 0.2127, -45.0905]
},
"leg_right3": {
"rotation": [9.83802, 2.5106, -9.9579]
},
"foot_right": {
"rotation": [-27.58324, "3.5552+math.sin(q.anim_time*90*2)*1.5", -20.1624]
},
"arm_left2": {
"rotation": [-4.71002, "-12.8789-math.sin(q.anim_time*90*2-90)*2", 28.88472]
},
"arm_left3": {
"rotation": [1.81234, "10.1983-math.sin(q.anim_time*90*2-30)*-5", 25.6704]
},
"arm_left": {
"rotation": [-33.31914, "-14.6364-math.sin(q.anim_time*90*2-60)*-2", 12.9264],
"position": [0, "0-math.sin(q.anim_time*90*2-30)*-0.3", 0]
},
"finger_front_left": {
"rotation": [0, -37.5, 20]
@@ -59,13 +201,14 @@
"rotation": [0, 37.5, 0]
},
"arm_right": {
"rotation": [-13.31914, 14.6364, -12.9264]
"rotation": [-33.31914, "14.6364+math.sin(q.anim_time*90*2-60)*-2", -12.9264],
"position": [0, "0-math.sin(q.anim_time*90*2-30)*-0.3", 0]
},
"arm_right2": {
"rotation": [-4.36302, 31.2646, -17.6253]
"rotation": [-4.71002, "12.8789+math.sin(q.anim_time*90*2-90)*2", -28.88472]
},
"arm_right3": {
"rotation": [1.80384, -2.387, -35.4292]
"rotation": [1.81234, "-10.1983+math.sin(q.anim_time*90*2-30)*-5", -25.6704]
},
"fingers_right": {
"rotation": [0, -37.5, 0]
@@ -80,37 +223,65 @@
"rotation": [0, 30, 27.5]
},
"torso": {
"rotation": [-2.5, 0, 0]
"rotation": ["12.5+math.sin(q.anim_time*90*2)*0.5", 0, 0],
"position": [0, "0+math.sin(q.anim_time*90*2+30)*-0.4", 0]
},
"chest": {
"rotation": [-7.5, 0, 0]
"rotation": ["-0+math.sin(q.anim_time*90*2-30)*1.5", 0, 0]
},
"hand_left": {
"rotation": [0, -17.5, 0]
"rotation": [0, "-17.5-math.sin(q.anim_time*90*2-60)*-2", 0]
},
"finger_front_left2": {
"rotation": [0, -12.5, 0]
"rotation": [0, "-12.5+math.sin(q.anim_time*90*2-150)*-5", 0]
},
"finger_middle_left2": {
"rotation": [0, -10, 0]
"rotation": [0, "-10+math.sin(q.anim_time*90*2-150)*-5", 0]
},
"finger_back_left2": {
"rotation": [0, -15, 0]
},
"head_angle": {
"rotation": [12.5, 0, 0]
"rotation": [0, "-15+math.sin(q.anim_time*90*2-150)*-5", 0]
},
"hand_right": {
"rotation": [0, 17.5, 0]
"rotation": [0, "17.5+math.sin(q.anim_time*90*2-60)*-2", 0]
},
"finger_front_right2": {
"rotation": [0, 12.5, 0]
"rotation": [0, "12.5+math.sin(q.anim_time*90*2-150)*5", 0]
},
"finger_middle_right2": {
"rotation": [0, 10, 0]
"rotation": [0, "10+math.sin(q.anim_time*90*2-150)*5", 0]
},
"finger_back_right2": {
"rotation": [0, 15, 0]
"rotation": [0, "15+math.sin(q.anim_time*90*2-150)*5", 0]
},
"check_left_spike_front": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*3", 0]
},
"check_left_spike_back": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*3", 0]
},
"check_right_spike_front": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*-3", 0]
},
"check_right_spike_back": {
"rotation": [0, "0+math.sin(q.anim_time*90*2-30)*-3", 0]
},
"eyebrow_left": {
"rotation": [0, "-2.5+math.sin(q.anim_time*90*2-60)*-2.5", 0]
},
"eyebrow_right": {
"rotation": [0, "2.5-math.sin(q.anim_time*90*2-60)*-2.5", 0]
},
"tail": {
"rotation": ["0+math.sin(q.anim_time*90*2-30)*-2.5", 0, 0]
},
"head": {
"rotation": ["-5+math.sin(q.anim_time*90*2-90)*-2.5", "math.sin(q.anim_time*90*1-90)*-0.5", 0]
},
"eye_angry_left": {
"position": [0, -0.1, 0]
},
"eye_angry_right": {
"position": [0, -0.1, 0]
}
}
},
@@ -233,14 +404,6 @@
}
}
},
"animation.swampert.cry": {
"animation_length": 1.2,
"sound_effects": {
"0.04": {
"effect": "pokemon.swampert.cry"
}
}
},
"animation.swampert.blink": {
"animation_length": 0.1667,
"bones": {
@@ -322,6 +485,158 @@
"position": [0, -0.1, 0]
}
}
},
"animation.swampert.cry": {
"animation_length": 2.5,
"bones": {
"chest2": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2917": {
"post": [7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-6, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0417": {
"post": [-1, 0, 0],
"lerp_mode": "catmullrom"
},
"2.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"neck": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.2917": {
"post": [7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5417": {
"post": [-6, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0417": {
"post": [-1, 0, 0],
"lerp_mode": "catmullrom"
},
"2.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"head": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5": {
"post": [-6, 0, 0],
"lerp_mode": "catmullrom"
},
"2.0": {
"post": [1, 0, 0],
"lerp_mode": "catmullrom"
},
"2.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.25": {
"pre": [0, 0, 0],
"post": [0, -0.1, 0]
},
"0.4167": {
"pre": [0, -0.1, 0],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.25": {
"pre": [0, 0, 0],
"post": [0, -0.1, 0]
},
"0.4167": {
"pre": [0, -0.1, 0],
"post": [0, 0, 0]
}
}
},
"jaw": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.6667": {
"post": [30, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [15, 0, 0],
"lerp_mode": "catmullrom"
},
"1.4167": {
"post": [40, 0, 0],
"lerp_mode": "catmullrom"
},
"1.8333": {
"post": [40, 0, 0],
"lerp_mode": "catmullrom"
},
"2.1667": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
}
},
"sound_effects": {
"0.4167": {
"effect": "pokemon.swampert.cry"
}
}
}
}
}
@@ -21,12 +21,12 @@
"rotation": [0, -45, 0]
},
"tail": {
"rotation": [50, 0, 0]
"rotation": ["50-math.sin(q.anim_time*90*2-60)*-3", 0, 0]
},
"toe_inner_left": {
"toe_front_inner_left": {
"rotation": [0, 5, 0]
},
"toe_outer_left": {
"toe_front_outer_left": {
"rotation": [0, -5, 0]
},
"neckflap_left": {
@@ -44,11 +44,372 @@
},
"wing_right3": {
"rotation": [0, 45, 0]
},
"torso": {
"rotation": ["-math.sin(q.anim_time*90*2-90)*1", 0, "math.sin(q.anim_time*90*1-90)*1"],
"position": [0, "math.sin(q.anim_time*90*2)*0.1", 0]
},
"neck": {
"rotation": ["-0-math.sin(q.anim_time*90*2-180)*1", 0, 0]
},
"head": {
"rotation": ["-0-math.sin(q.anim_time*90*2-90)*-2", 0, 0]
},
"hair": {
"rotation": ["-0-math.sin(q.anim_time*90*2-150)*-2", 0, 0]
}
}
},
"animation.squawkabilly.ground_walk": {
"loop": true,
"animation_length": 0.75,
"bones": {
"hairstrand": {
"rotation": [7.5, 0, 12.5]
},
"hairstrand2": {
"rotation": [-17.5, 0, 0]
},
"wing_left": {
"rotation": [155.5391, -73.26807, -64.02788],
"position": [0, -1.5, -2]
},
"wing_left2": {
"rotation": [0, 45, 0]
},
"wing_left3": {
"rotation": [0, -45, 0]
},
"tail": {
"rotation": ["50-math.cos(q.anim_time*90*16/3*2)*5", 0, 0]
},
"toe_front_inner_left": {
"rotation": {
"0.0": [-55, 10, -7.5],
"0.125": {
"pre": [7.5, 15, 0],
"post": [7.5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, 15, 0],
"0.625": [0, 15, 0],
"0.75": [-55, 10, -7.5]
},
"position": {
"0.0": [0, -0.75, 0.85],
"0.125": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [0, -0.75, 0.85]
}
},
"toe_front_outer_left": {
"rotation": {
"0.0": [-51.5, -2.5, 10],
"0.125": {
"pre": [5, -15, 0],
"post": [5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, -15, 0],
"0.625": [0, -15, 0],
"0.75": [-51.5, -2.5, 10]
},
"position": {
"0.0": [0, -0.75, 0.85],
"0.125": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [0, -0.75, 0.85]
}
},
"neckflap_left": {
"rotation": [0, 0, "15+math.cos(q.anim_time*90*16/3*2-60)*-2.5"]
},
"neckflap_right": {
"rotation": [0, 0, "-15-math.cos(q.anim_time*90*16/3*2-60)*-2.5"]
},
"wing_right": {
"rotation": [153.0391, 73.26807, 64.02788],
"position": [0, -2, -3]
},
"wing_right2": {
"rotation": [0, -45, 0]
},
"wing_right3": {
"rotation": [0, 45, 0]
},
"torso": {
"rotation": ["1-math.cos(q.anim_time*90*16/3*2)*-2", "-math.sin(q.anim_time*90*16/3+60)*3", "-1+math.sin(q.anim_time*90*16/3+30)*-4"],
"position": [0, "math.sin(q.anim_time*90*16/3*2)*0.2", 0]
},
"coat_left": {
"rotation": ["-0-math.cos(q.anim_time*90*16/3*2)*-2.5", 0, "0+math.cos(q.anim_time*90*8/3*2)*5"]
},
"coat_right": {
"rotation": ["-0-math.cos(q.anim_time*90*16/3*2)*-2.5", 0, "0-math.cos(q.anim_time*90*8/3*2)*-5"]
},
"neck": {
"rotation": ["-math.cos(q.anim_time*90*16/3*2)*-2.5", "-math.sin(q.anim_time*90*16/3+60)*-3", "math.sin(q.anim_time*90*16/3+30)*3"]
},
"head": {
"rotation": ["-0-math.cos(q.anim_time*90*16/3*2)*3", 0, 0]
},
"hair": {
"rotation": ["-0-math.cos(q.anim_time*90*16/3*2-60)*4", 0, 0]
},
"leg_left": {
"rotation": {
"0.0": [25, -17.5, 0],
"0.125": {
"pre": [21.25, -12.5, 0],
"post": [21.25, -12.5, 0],
"lerp_mode": "catmullrom"
},
"0.375": [-40, -5, 0],
"0.75": [25, -17.5, 0]
},
"position": {
"0.0": [0.3, 0.1, 0.6],
"0.0417": [0.23, 0.25, 0.71],
"0.125": {
"pre": [0, 0.68, -0.08],
"post": [0, 0.68, -0.08],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [0, 0.97, -1.41],
"lerp_mode": "catmullrom"
},
"0.375": [0, -0.5, -2.15],
"0.5": [0, -0.19, -1.02],
"0.5833": [0, -0.07, -0.3],
"0.625": [0, -0.02, 0.21],
"0.7083": [0.1, 0.215, 0.43],
"0.75": [0.3, 0.1, 0.6]
}
},
"leg_left2": {
"rotation": {
"0.0": [0, 0, 0],
"0.125": {
"pre": [-30.83, 0, 0],
"post": [-30.83, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [-2.65, 0, 0],
"lerp_mode": "catmullrom"
},
"0.375": [12.5, 0, 0],
"0.75": [0, 0, 0]
}
},
"foot_left": {
"rotation": {
"0.0": [27.5, 0, -5],
"0.125": {
"pre": [20.17, 0, 0],
"post": [20.17, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [12.596, 0.6637, -1.3486],
"lerp_mode": "catmullrom"
},
"0.375": [28, 0, 0],
"0.625": [-7.33, 0, 0],
"0.75": [27.5, 0, -5]
},
"position": {
"0.0": [0, 0.25, 0],
"0.125": [0, 0, 0],
"0.375": [0, 0, 0],
"0.625": [0, 0.17, 0],
"0.75": [0, 0.25, 0]
}
},
"toe_back_outer_left": {
"rotation": {
"0.0": [-20, 0, 0],
"0.2083": {
"pre": [-8.33, 0, 0],
"post": [-8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [-20, 0, 0]
}
},
"leg_right": {
"rotation": {
"0.0": [-40, 5, 0],
"0.375": [25, 17.5, 0],
"0.5": {
"pre": [21.25, 12.5, 0],
"post": [21.25, 12.5, 0],
"lerp_mode": "catmullrom"
},
"0.75": [-40, 5, 0]
},
"position": {
"0.0": [0, -0.5, -2.15],
"0.125": [0, -0.19, -1.02],
"0.2083": [0, -0.07, -0.3],
"0.25": [0, -0.02, 0.21],
"0.3333": [-0.1, 0.215, 0.43],
"0.375": [-0.3, 0.1, 0.6],
"0.4167": [-0.23, 0.25, 0.71],
"0.5": {
"pre": [0, 0.68, -0.08],
"post": [0, 0.68, -0.08],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [0, 0.97, -1.41],
"lerp_mode": "catmullrom"
},
"0.75": [0, -0.5, -2.15]
}
},
"leg_right2": {
"rotation": {
"0.0": [12.5, 0, 0],
"0.375": [0, 0, 0],
"0.5": {
"pre": [-30.83, 0, 0],
"post": [-30.83, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [-2.65, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": [12.5, 0, 0]
}
},
"foot_right": {
"rotation": {
"0.0": [28, 0, 0],
"0.25": [-7.33, 0, 0],
"0.375": [27.5, 0, 5],
"0.5": {
"pre": [20.17, 0, 0],
"post": [20.17, 0, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [12.596, -0.6637, 1.3486],
"lerp_mode": "catmullrom"
},
"0.75": [28, 0, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 0.17, 0],
"0.375": [0, 0.25, 0],
"0.5": [0, 0, 0],
"0.75": [0, 0, 0]
}
},
"toe_back_inner_right": {
"rotation": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [-20, 0, 0],
"0.5833": {
"pre": [-8.33, 0, 0],
"post": [-8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, 0, 0]
}
},
"toe_front_inner_right": {
"rotation": {
"0.0": [0, -15, 0],
"0.25": [0, -15, 0],
"0.375": [-55, -10, 7.5],
"0.5": {
"pre": [7.5, -15, 0],
"post": [7.5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [5, -15, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, -15, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [0, -0.75, 0.85],
"0.5": [0, 0, 0],
"0.75": [0, 0, 0]
}
},
"toe_front_outer_right": {
"rotation": {
"0.0": [0, 15, 0],
"0.25": [0, 15, 0],
"0.375": [-51.5, 2.5, -10],
"0.5": {
"pre": [5, 15, 0],
"post": [5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.625": {
"post": [5, 15, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, 15, 0]
},
"position": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [0, -0.75, 0.85],
"0.5": [0, 0, 0],
"0.75": [0, 0, 0]
}
},
"toe_back_inner_left": {
"rotation": {
"0.0": [-20, 0, 0],
"0.2083": {
"pre": [-8.33, 0, 0],
"post": [-8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"0.375": [0, 0, 0],
"0.625": [0, 0, 0],
"0.75": [-20, 0, 0]
}
},
"toe_back_inner_right3": {
"rotation": {
"0.0": [0, 0, 0],
"0.25": [0, 0, 0],
"0.375": [-20, 0, 0],
"0.5833": {
"pre": [-8.33, 0, 0],
"post": [-8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"0.75": [0, 0, 0]
}
}
}
},
"animation.squawkabilly.cry": {
"animation_length": 1.25,
"animation_length": 2,
"bones": {
"neck": {
"rotation": {
@@ -56,15 +417,27 @@
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.1667": {
"post": [10, 0, 0],
"0.25": {
"post": [12.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [10, 0, 0],
"0.5": {
"post": [2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [8.33, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-2.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.9167": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
@@ -76,15 +449,27 @@
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.1667": {
"post": [-15, 0, 0],
"0.25": {
"post": [7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [-15, 0, 0],
"0.5": {
"post": [-20, 0, 0],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [-12.78, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-27.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.75": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
@@ -96,19 +481,118 @@
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.1667": {
"0.25": {
"post": [25, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [25, 0, 0],
"0.5": {
"post": [47.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [20, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [62.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"torso": {
"rotation": {
"0.0": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.25": {
"post": [5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [-7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.8333": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.25": {
"post": [-5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5833": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.9167": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"hair": {
"rotation": {
"0.0833": {
"pre": [0, 0, 0],
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"0.3333": {
"post": [7.5, 0, 0],
"lerp_mode": "catmullrom"
},
"0.5833": {
"post": [-20, 0, 0],
"lerp_mode": "catmullrom"
},
"0.9167": {
"post": [-12.78, 0, 0],
"lerp_mode": "catmullrom"
},
"1.3333": {
"post": [-27.5, 0, 0],
"lerp_mode": "catmullrom"
},
"1.5833": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
},
"1.8333": {
"post": [0, 0, 0],
"lerp_mode": "catmullrom"
}
}
},
"eyelid_left": {
"position": {
"0.0": [0, 0, 0],
"0.8333": {
"pre": [0, 0, 0],
"post": [0.1, 0, -0.1]
},
"1.0": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [0, 0, 0],
"0.8333": {
"pre": [0, 0, 0],
"post": [-0.1, 0, -0.1]
},
"1.0": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
}
},
"sound_effects": {
@@ -126,18 +610,205 @@
"eyelid_left": {
"position": {
"0.0": [0.1, 0, -0.1],
"0.125": [0.1, 0, -0.1],
"0.1667": [0, 0, 0]
"0.1667": {
"pre": [0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
},
"eyelid_right": {
"position": {
"0.0": [-0.1, 0, -0.1],
"0.125": [-0.1, 0, -0.1],
"0.1667": [0, 0, 0]
"0.1667": {
"pre": [-0.1, 0, -0.1],
"post": [0, 0, 0]
}
}
}
}
},
"animation.squawkabilly.air_idle": {
"loop": true,
"animation_length": 4,
"bones": {
"body": {
"position": ["-math.sin(q.anim_time*90*3-45)*0.5", "math.sin(q.anim_time*90*6-45)*1.5", "math.sin(q.anim_time*90*6-45)*1/2"]
},
"coat_left": {
"rotation": ["-0-math.sin(q.anim_time*90*6-60)*3", 0, "0+math.sin(q.anim_time*90*6-90)*5"]
},
"coat_right": {
"rotation": ["-0-math.sin(q.anim_time*90*6-60)*3", 0, "-0-math.sin(q.anim_time*90*6-90)*5"]
},
"wing_left": {
"rotation": ["25-math.sin(q.anim_time*90*6-90)*6", 0, "math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_left2": {
"rotation": [0, 0, "20+math.sin(q.anim_time*90*6-45)*30"]
},
"wing_left3": {
"rotation": [0, 0, "0+math.sin(q.anim_time*90*6-75)*30"]
},
"wing_right": {
"rotation": ["25-math.sin(q.anim_time*90*6-90)*6", 0, "-math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_right2": {
"rotation": [0, 0, "-20-math.sin(q.anim_time*90*6-45)*30"]
},
"wing_right3": {
"rotation": [0, 0, "-0-math.sin(q.anim_time*90*6-75)*30"]
},
"tail": {
"rotation": ["15-math.sin(q.anim_time*90*6-30)*7", 0, 0]
},
"neck": {
"rotation": ["5-math.sin(q.anim_time*90*6-140)*-1", 0, "math.sin(q.anim_time*90*3-00)*-1"]
},
"head": {
"rotation": ["-math.sin(q.anim_time*90*6-110)*3", 0, "math.sin(q.anim_time*90*3-120)*-2"]
},
"hair": {
"rotation": ["-0-math.sin(q.anim_time*90*6-170)*5", 0, 0]
},
"leg_left": {
"rotation": [-67.5, 0, -2.5]
},
"foot_left": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", -10, "math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_outer_left": {
"rotation": [-30, 0, 0]
},
"toe_front_inner_left": {
"rotation": [30, 0, 0]
},
"toe_front_outer_left": {
"rotation": [30, 0, 0]
},
"leg_right": {
"rotation": [-67.5, 0, 2.5]
},
"foot_right": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", 10, "-math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_inner_right": {
"rotation": [-30, 0, 0]
},
"toe_front_inner_right": {
"rotation": [30, 0, 0]
},
"toe_front_outer_right": {
"rotation": [30, 0, 0]
},
"toe_back_inner_left": {
"rotation": [-30, 0, 0]
},
"toe_back_inner_right3": {
"rotation": [-30, 0, 0]
}
},
"timeline": {
"0.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"1.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"3.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"4.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};"
}
},
"animation.squawkabilly.air_fly": {
"loop": true,
"animation_length": 4,
"bones": {
"body": {
"rotation": [25, 0, 0],
"position": ["-math.sin(q.anim_time*90*3-45)*0.5", "math.sin(q.anim_time*90*6-45)*1.5", "math.sin(q.anim_time*90*6-45)*1/2"]
},
"coat_left": {
"rotation": ["-0-math.sin(q.anim_time*90*6-60)*3", 0, "0+math.sin(q.anim_time*90*6-90)*5"]
},
"coat_right": {
"rotation": ["-0-math.sin(q.anim_time*90*6-60)*3", 0, "-0-math.sin(q.anim_time*90*6-90)*5"]
},
"wing_left": {
"rotation": ["-0-math.sin(q.anim_time*90*6-90)*6", -5, "math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_left2": {
"rotation": [0, 0, "20+math.sin(q.anim_time*90*6-45)*30"]
},
"wing_left3": {
"rotation": [0, 0, "0+math.sin(q.anim_time*90*6-75)*30"]
},
"wing_right": {
"rotation": ["-0-math.sin(q.anim_time*90*6-90)*6", 5, "-math.sin(q.anim_time*90*6)*65"],
"position": [0, "math.sin(q.anim_time*90*6)*-1", 0]
},
"wing_right2": {
"rotation": [0, 0, "-20-math.sin(q.anim_time*90*6-45)*30"]
},
"wing_right3": {
"rotation": [0, 0, "-0-math.sin(q.anim_time*90*6-75)*30"]
},
"tail": {
"rotation": ["15-math.sin(q.anim_time*90*6-30)*7", 0, 0]
},
"neck": {
"rotation": ["25-math.sin(q.anim_time*90*6-140)*-1", 0, "math.sin(q.anim_time*90*3-00)*-1"]
},
"head": {
"rotation": ["-50-math.sin(q.anim_time*90*6-110)*3", 0, "math.sin(q.anim_time*90*3-120)*-2"]
},
"hair": {
"rotation": ["-0-math.sin(q.anim_time*90*6-170)*5", 0, 0]
},
"leg_left": {
"rotation": [-67.5, 0, -2.5]
},
"foot_left": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", -10, "math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_outer_left": {
"rotation": [-30, 0, 0]
},
"toe_front_inner_left": {
"rotation": [30, 0, 0]
},
"toe_front_outer_left": {
"rotation": [30, 0, 0]
},
"leg_right": {
"rotation": [-67.5, 0, 2.5]
},
"foot_right": {
"rotation": ["40-math.sin(q.anim_time*90*6-30)*-5", 10, "-math.sin(q.anim_time*90*3-150)*6"]
},
"toe_back_inner_right": {
"rotation": [-30, 0, 0]
},
"toe_front_inner_right": {
"rotation": [30, 0, 0]
},
"toe_front_outer_right": {
"rotation": [30, 0, 0]
},
"toe_back_inner_left": {
"rotation": [-30, 0, 0]
},
"toe_back_inner_right3": {
"rotation": [-30, 0, 0]
}
},
"timeline": {
"0.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"1.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"2.6667": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"3.3333": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};",
"4.0": "q.has_entity?{q.sound('animation.plumage.wing_flap.small');};"
}
}
}
}
@@ -68,7 +68,12 @@
"name": "head",
"parent": "neck3",
"pivot": [0, 32.86868, -14.9296],
"rotation": [-90, 0, 0],
"rotation": [-90, 0, 0]
},
{
"name": "head_ai",
"parent": "head",
"pivot": [0, 32.86868, -14.9296],
"cubes": [
{"origin": [-2.5, 29.75372, -22.00071], "size": [5, 6, 8], "uv": [174, 92]},
{"origin": [-2.51, 34.75372, -22.00071], "size": [1, 1, 3], "inflate": 0.03, "uv": [1, 217]},
@@ -77,7 +82,7 @@
},
{
"name": "eyes",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 33.75372, -20.50071]
},
{
@@ -226,7 +231,7 @@
},
{
"name": "beak",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 33.35372, -22.50071],
"rotation": [17.5, 0, 0],
"cubes": [
@@ -276,7 +281,7 @@
},
{
"name": "crest",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 35.85372, -18.60071],
"rotation": [-5, 0, 0]
},
@@ -322,7 +327,7 @@
},
{
"name": "jaw",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 31.85372, -22.40071],
"rotation": [5, 0, 0],
"cubes": [
@@ -340,7 +345,7 @@
},
{
"name": "locator_head",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 36, -20],
"locators": {
"head": [0, 36, -20]
@@ -348,7 +353,7 @@
},
{
"name": "locator_item_hat",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 39, -18],
"locators": {
"item_hat": [0, 39, -18]
@@ -356,7 +361,7 @@
},
{
"name": "locator_item_face",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 34.25, -22],
"locators": {
"item_face": [0, 34.25, -22]
@@ -364,7 +369,7 @@
},
{
"name": "locator_face",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 31, -34],
"locators": {
"face": [0, 31, -34]
@@ -372,7 +377,7 @@
},
{
"name": "locator_mouth",
"parent": "head",
"parent": "head_ai",
"pivot": [0, 31.75, -20],
"locators": {
"mouth": [0, 31.75, -20]
@@ -7,8 +7,8 @@
"texture_width": 256,
"texture_height": 128,
"visible_bounds_width": 6,
"visible_bounds_height": 4,
"visible_bounds_offset": [0, 1, 0]
"visible_bounds_height": 5,
"visible_bounds_offset": [0, 1.5, 0]
},
"bones": [
{
@@ -252,7 +252,7 @@
"name": "head_correction",
"parent": "head",
"pivot": [0, 14.25, -14],
"rotation": [-42.5, 0, 0]
"rotation": [42.5, 0, 0]
},
{
"name": "head_ai",
@@ -263,6 +263,7 @@
"name": "head_angle",
"parent": "head_ai",
"pivot": [0, 14.25, -15],
"rotation": [-90, 0, 0],
"cubes": [
{"origin": [-7, 3.5, -21.5], "size": [14, 15, 6], "inflate": 0.01, "uv": [0, 0]},
{"origin": [-7, 10.5, -15.5], "size": [14, 8, 3], "inflate": 0.01, "uv": [40, 0]},
@@ -227,6 +227,14 @@
{"origin": [3.1, 20.8, -0.84], "size": [2, 1, 1], "inflate": 0.1, "uv": [60, 42]}
]
},
{
"name": "eyelid_closed_left",
"parent": "head_left",
"pivot": [3.6, 19.7, -0.91],
"cubes": [
{"origin": [2.1, 17.2, -0.66], "size": [3, 5, 1], "inflate": 0.25, "uv": [98, 2]}
]
},
{
"name": "face_ditto_left",
"parent": "head_left",
@@ -332,6 +340,14 @@
{"origin": [-5.1, 20.8, -0.84], "size": [2, 1, 1], "inflate": 0.1, "uv": [51, 42], "mirror": true}
]
},
{
"name": "eyelid_closed_right",
"parent": "head_right",
"pivot": [-3.6, 19.7, -0.91],
"cubes": [
{"origin": [-5.1, 17.2, -0.66], "size": [3, 5, 1], "inflate": 0.25, "uv": [89, 2], "mirror": true}
]
},
{
"name": "face_ditto_right",
"parent": "head_right",
@@ -134,229 +134,114 @@
"jaw2": [0, 27, -7.35]
}
},
{
"name": "head_right",
"parent": "head_angle",
"pivot": [-3.525, 31, 0.25],
"rotation": [5, 0, 0],
"cubes": [
{"origin": [-4.525, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 11]},
{"origin": [-3.625, 31.5, 0.25], "size": [0, 3, 5], "uv": [52, 55]}
]
},
{
"name": "locator_eye_right",
"parent": "head_right",
"pivot": [-4.5, 31, -2.5],
"rotation": [0, 90, 0],
"locators": {
"eye1": [-4.5, 31, -2.5]
}
},
{
"name": "eye_right",
"parent": "head_right",
"pivot": [-4.525, 31.35, -2.25],
"cubes": [
{"origin": [-4.525, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [66, 2], "mirror": true}
]
},
{
"name": "eye_neutral_right",
"parent": "eye_right",
"pivot": [-4.525, 32.85095, -2.27179],
"cubes": [
{"origin": [-4.505, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [66, 12], "mirror": true}
]
},
{
"name": "eye_angry_right",
"parent": "eye_right",
"pivot": [-4.435, 32.1, -2.5],
"cubes": [
{"origin": [-4.405, 30.85, -4.25], "size": [1, 3, 4], "inflate": 0.05, "uv": [66, 26], "mirror": true},
{"origin": [-4.44, 31.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [73, 27], "mirror": true}
]
},
{
"name": "eye_sad_right",
"parent": "eye_right",
"pivot": [-4.435, 32.85, -2.5],
"cubes": [
{"origin": [-4.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [66, 19], "mirror": true},
{"origin": [-4.44, 32.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [73, 20], "mirror": true}
]
},
{
"name": "pupil_right",
"parent": "eye_right",
"pivot": [-4.525, 30.85095, -3.02179],
"cubes": [
{"origin": [-4.525, 29.35, -4.04], "size": [1, 3, 2], "inflate": 0.02, "uv": [73, 0], "mirror": true}
]
},
{
"name": "pupil_right2",
"parent": "pupil_right",
"pivot": [-4.55, 31.35095, -3.02179],
"cubes": [
{"origin": [-4.55, 30.35, -3.55], "size": [1, 2, 1], "uv": [84, 2], "mirror": true}
]
},
{
"name": "eyelid_right",
"parent": "head_right",
"pivot": [-3.925, 31.35095, -2.27179],
"cubes": [
{"origin": [-4.425, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [77, 2], "mirror": true}
]
},
{
"name": "pupil_eyelid_right",
"parent": "eyelid_right",
"pivot": [-4.425, 30.80095, -2.27179],
"cubes": [
{"origin": [-4.435, 30.3, -3.75], "size": [1, 1, 3], "inflate": 0.01, "uv": [77, 14], "mirror": true}
]
},
{
"name": "eyelid_right2",
"parent": "eyelid_right",
"pivot": [-3.925, 32.85095, -2.27179],
"cubes": [
{"origin": [-4.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [66, 12], "mirror": true}
]
},
{
"name": "pupil_happy_right",
"parent": "eyelid_right",
"pivot": [-4.325, 30.80095, -2.27179],
"cubes": [
{"origin": [-4.325, 30.30095, -3.77179], "size": [1, 2, 3], "inflate": 0.05, "uv": [77, 20], "mirror": true}
]
},
{
"name": "face_ditto_right",
"parent": "head_right",
"pivot": [-4.435, 32, -2.25]
},
{
"name": "eye_ditto_right",
"parent": "face_ditto_right",
"pivot": [-4.435, 32, -2.25],
"cubes": [
{"origin": [-4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [113, 1]}
]
},
{
"name": "eye_closed_ditto_right",
"parent": "face_ditto_right",
"pivot": [-4.335, 32, -2.25],
"cubes": [
{"origin": [-4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [112, 2]},
{"origin": [-4.345, 32, -2.75], "size": [0, 1, 1], "uv": [113, 5]}
]
},
{
"name": "head_left",
"parent": "head_angle",
"pivot": [3.525, 31, 0.25],
"pivot": [3.53, 31, 0.25],
"rotation": [5, 0, 0],
"cubes": [
{"origin": [2.525, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 21], "mirror": true},
{"origin": [3.625, 31.5, 0.25], "size": [0, 3, 5], "uv": [40, 55], "mirror": true}
{"origin": [2.53, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 21], "mirror": true},
{"origin": [3.63, 31.5, 0.25], "size": [0, 3, 5], "uv": [40, 55], "mirror": true}
]
},
{
"name": "locator_eye_left",
"parent": "head_left",
"pivot": [4.5, 31, -2.5],
"rotation": [0, -90, 0],
"locators": {
"eye2": [4.5, 31, -2.5]
}
},
{
"name": "eye_left",
"parent": "head_left",
"pivot": [4.525, 31.35, -2.25],
"pivot": [4.53, 31.5, -2.25],
"cubes": [
{"origin": [3.525, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [88, 2]}
{"origin": [3.54, 29, -4.25], "size": [1, 5, 4], "uv": [88, 2]}
]
},
{
"name": "eye_neutral_left",
"parent": "eye_left",
"pivot": [4.525, 32.85095, -2.27179],
"pivot": [4.54, 33, -2.25],
"cubes": [
{"origin": [3.505, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 12]}
{"origin": [3.54, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [88, 12]}
]
},
{
"name": "eye_angry_left",
"parent": "eye_left",
"pivot": [4.435, 32.1, -2.5],
"pivot": [4.44, 32.5, -2.25],
"cubes": [
{"origin": [3.405, 30.85, -4.25], "size": [1, 3, 4], "inflate": 0.05, "uv": [88, 26]},
{"origin": [3.44, 31.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [95, 27]}
{"origin": [3.44, 31, -4.25], "size": [1, 3, 4], "inflate": 0.021, "uv": [88, 26]},
{"origin": [3.44, 31.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [95, 27]}
]
},
{
"name": "eye_sad_left",
"parent": "eye_left",
"pivot": [4.435, 32.85, -2.5],
"pivot": [4.43, 33, -2.25],
"cubes": [
{"origin": [3.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 19]},
{"origin": [3.44, 32.35, -3.27], "size": [1, 1, 1], "inflate": 0.02, "uv": [95, 20]}
{"origin": [3.44, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [88, 19]},
{"origin": [3.44, 32.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [95, 20]}
]
},
{
"name": "pupil_left",
"parent": "eye_left",
"pivot": [4.525, 30.85095, -3.02179],
"pivot": [4.55, 31, -3.05],
"cubes": [
{"origin": [3.525, 29.35, -4.04], "size": [1, 3, 2], "inflate": 0.02, "uv": [95, 0]}
{"origin": [3.55, 29.5, -4.05], "size": [1, 3, 2], "uv": [95, 0]}
]
},
{
"name": "pupil_left2",
"name": "eye_shine_left",
"parent": "pupil_left",
"pivot": [4.55, 31.35095, -3.02179],
"pivot": [4.55, 31.5, -3.05],
"cubes": [
{"origin": [3.55, 30.35, -3.55], "size": [1, 2, 1], "uv": [106, 2]}
{"origin": [3.56, 30.5, -3.55], "size": [1, 2, 1], "uv": [106, 2]}
]
},
{
"name": "open_eye_left",
"parent": "head_left",
"pivot": [4.44, 31.5, -2.25],
"cubes": [
{"origin": [3.44, 29, -4.25], "size": [1, 5, 4], "uv": [99, 2]}
]
},
{
"name": "open_pupil_left",
"parent": "open_eye_left",
"pivot": [4.45, 31.5, -2.25],
"cubes": [
{"origin": [3.45, 31, -3.75], "size": [1, 1, 3], "uv": [99, 14]}
]
},
{
"name": "open_neutral_left",
"parent": "open_eye_left",
"pivot": [4.34, 33, -2.25],
"cubes": [
{"origin": [3.34, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [88, 12]}
]
},
{
"name": "open_happy_left",
"parent": "open_eye_left",
"pivot": [4.35, 32, -2.25],
"cubes": [
{"origin": [3.35, 31, -3.75], "size": [1, 2, 3], "uv": [99, 20]}
]
},
{
"name": "eyelid_left",
"parent": "head_left",
"pivot": [3.925, 31.35095, -2.27179],
"pivot": [4.44, 31.5, -2.25],
"cubes": [
{"origin": [3.425, 28.85, -4.25], "size": [1, 5, 4], "inflate": 0.01, "uv": [99, 2]}
{"origin": [3.44, 29, -4.25], "size": [1, 5, 4], "inflate": 0.022, "uv": [99, 26]}
]
},
{
"name": "pupil_eyelid_left",
"parent": "eyelid_left",
"pivot": [4.425, 30.80095, -2.27179],
"cubes": [
{"origin": [3.435, 30.3, -3.75], "size": [1, 1, 3], "inflate": 0.01, "uv": [99, 14]}
]
},
{
"name": "eyelid_left2",
"parent": "eyelid_left",
"pivot": [3.925, 32.85095, -2.27179],
"cubes": [
{"origin": [3.405, 31.85, -4.25], "size": [1, 2, 4], "inflate": 0.05, "uv": [88, 12]}
]
},
{
"name": "pupil_happy_left",
"parent": "eyelid_left",
"pivot": [4.325, 30.80095, -2.27179],
"cubes": [
{"origin": [3.325, 30.30095, -3.77179], "size": [1, 2, 3], "inflate": 0.05, "uv": [99, 20]}
]
"name": "locator_eye_left",
"parent": "head_left",
"pivot": [4.54, 31, -2.46],
"rotation": [0, -90, 0],
"locators": {
"eye2": [4.54, 31, -2.46]
}
},
{
"name": "face_ditto_left",
@@ -368,7 +253,7 @@
"parent": "face_ditto_left",
"pivot": [4.435, 32, -2.25],
"cubes": [
{"origin": [4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [118, 1], "mirror": true}
{"origin": [4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [113, 1]}
]
},
{
@@ -376,8 +261,139 @@
"parent": "face_ditto_left",
"pivot": [4.335, 32, -2.25],
"cubes": [
{"origin": [4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [117, 2], "mirror": true},
{"origin": [4.345, 32, -2.75], "size": [0, 1, 1], "uv": [118, 5], "mirror": true}
{"origin": [4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [112, 2]},
{"origin": [4.345, 32, -2.75], "size": [0, 1, 1], "uv": [113, 5]}
]
},
{
"name": "head_right",
"parent": "head_angle",
"pivot": [-3.53, 31, 0.25],
"rotation": [5, 0, 0],
"cubes": [
{"origin": [-4.53, 29.5, -4.75], "size": [2, 5, 5], "uv": [51, 11]},
{"origin": [-3.63, 31.5, 0.25], "size": [0, 3, 5], "uv": [40, 55]}
]
},
{
"name": "eye_right",
"parent": "head_right",
"pivot": [-4.53, 31.5, -2.25],
"cubes": [
{"origin": [-4.54, 29, -4.25], "size": [1, 5, 4], "uv": [66, 2], "mirror": true}
]
},
{
"name": "eye_neutral_right",
"parent": "eye_right",
"pivot": [-4.54, 33, -2.25],
"cubes": [
{"origin": [-4.54, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [66, 12], "mirror": true}
]
},
{
"name": "eye_angry_right",
"parent": "eye_right",
"pivot": [-4.44, 32.5, -2.25],
"cubes": [
{"origin": [-4.44, 31, -4.25], "size": [1, 3, 4], "inflate": 0.021, "uv": [66, 26], "mirror": true},
{"origin": [-4.44, 31.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [73, 27], "mirror": true}
]
},
{
"name": "eye_sad_right",
"parent": "eye_right",
"pivot": [-4.43, 33, -2.25],
"cubes": [
{"origin": [-4.44, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [66, 19], "mirror": true},
{"origin": [-4.44, 32.5, -3.25], "size": [1, 1, 1], "inflate": 0.03, "uv": [73, 20], "mirror": true}
]
},
{
"name": "pupil_right",
"parent": "eye_right",
"pivot": [-4.55, 31, -3.05],
"cubes": [
{"origin": [-4.55, 29.5, -4.05], "size": [1, 3, 2], "uv": [73, 0], "mirror": true}
]
},
{
"name": "eye_shine_right",
"parent": "pupil_right",
"pivot": [-4.55, 31.5, -3.05],
"cubes": [
{"origin": [-4.56, 30.5, -3.55], "size": [1, 2, 1], "uv": [84, 2], "mirror": true}
]
},
{
"name": "open_eye_right",
"parent": "head_right",
"pivot": [-4.44, 31.5, -2.25],
"cubes": [
{"origin": [-4.44, 29, -4.25], "size": [1, 5, 4], "uv": [77, 2], "mirror": true}
]
},
{
"name": "open_pupil_right",
"parent": "open_eye_right",
"pivot": [-4.45, 31.5, -2.25],
"cubes": [
{"origin": [-4.45, 31, -3.75], "size": [1, 1, 3], "uv": [77, 14], "mirror": true}
]
},
{
"name": "open_neutral_right",
"parent": "open_eye_right",
"pivot": [-4.34, 33, -2.25],
"cubes": [
{"origin": [-4.34, 32, -4.25], "size": [1, 2, 4], "inflate": 0.021, "uv": [66, 12], "mirror": true}
]
},
{
"name": "open_happy_right",
"parent": "open_eye_right",
"pivot": [-4.35, 32, -2.25],
"cubes": [
{"origin": [-4.35, 31, -3.75], "size": [1, 2, 3], "uv": [77, 20], "mirror": true}
]
},
{
"name": "eyelid_right",
"parent": "head_right",
"pivot": [-4.44, 31.5, -2.25],
"cubes": [
{"origin": [-4.44, 29, -4.25], "size": [1, 5, 4], "inflate": 0.022, "uv": [77, 26], "mirror": true}
]
},
{
"name": "locator_eye_right",
"parent": "head_right",
"pivot": [-4.54, 31, -2.46],
"rotation": [0, 90, 0],
"locators": {
"eye1": [-4.54, 31, -2.46]
}
},
{
"name": "face_ditto_right",
"parent": "head_right",
"pivot": [-4.435, 32, -2.25]
},
{
"name": "eye_ditto_right",
"parent": "face_ditto_right",
"pivot": [-4.435, 32, -2.25],
"cubes": [
{"origin": [-4.435, 31.5, -2.75], "size": [0, 1, 1], "uv": [118, 1], "mirror": true}
]
},
{
"name": "eye_closed_ditto_right",
"parent": "face_ditto_right",
"pivot": [-4.335, 32, -2.25],
"cubes": [
{"origin": [-4.335, 31.5, -3.25], "size": [0, 1, 2], "uv": [117, 2], "mirror": true},
{"origin": [-4.345, 32, -2.75], "size": [0, 1, 1], "uv": [118, 5], "mirror": true}
]
},
{
@@ -830,11 +830,6 @@
{"origin": [-3.5, 38.3, 33.75], "size": [7, 5, 9], "uv": [106, 33]}
]
},
{
"name": "tongue6",
"parent": "tongue5",
"pivot": [0, 40.8, 40.5]
},
{
"name": "muzzle",
"parent": "head_angle",
@@ -913,6 +908,15 @@
{"origin": [-0.1, 44.3, -8.11], "size": [4, 2, 1], "uv": [87, 72]}
]
},
{
"name": "eyelid_base_left",
"parent": "eye_left_base",
"pivot": [1.9, 43.9, -8.11],
"cubes": [
{"origin": [-0.1, 45.3, -8.11], "size": [4, 1, 1], "uv": [87, 76]},
{"origin": [-0.1, 41.9, -8.11], "size": [4, 4, 1], "uv": [87, 79]}
]
},
{
"name": "eye_left",
"parent": "eye_left_base",
@@ -950,7 +954,7 @@
"parent": "eye_left",
"pivot": [1.9, 44.2, -8.07],
"cubes": [
{"origin": [0.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [87, 76]}
{"origin": [0.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [87, 85]}
]
},
{
@@ -958,7 +962,7 @@
"parent": "eye_left",
"pivot": [1.9, 44.2, -8.07],
"cubes": [
{"origin": [0.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [87, 79]}
{"origin": [0.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [87, 88]}
]
},
{
@@ -995,6 +999,15 @@
{"origin": [-3.9, 44.3, -8.11], "size": [4, 2, 1], "uv": [76, 72], "mirror": true}
]
},
{
"name": "eyelid_base_right",
"parent": "eye_right_base",
"pivot": [-1.9, 43.9, -8.11],
"cubes": [
{"origin": [-3.9, 45.3, -8.11], "size": [4, 1, 1], "uv": [76, 76], "mirror": true},
{"origin": [-3.9, 41.9, -8.11], "size": [4, 4, 1], "uv": [76, 79], "mirror": true}
]
},
{
"name": "eye_right",
"parent": "eye_right_base",
@@ -1032,7 +1045,7 @@
"parent": "eye_right",
"pivot": [-1.9, 44.2, -8.07],
"cubes": [
{"origin": [-3.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [76, 76], "mirror": true}
{"origin": [-3.4, 43.7, -8.07], "size": [3, 1, 1], "inflate": 0.02, "uv": [76, 85], "mirror": true}
]
},
{
@@ -1040,7 +1053,7 @@
"parent": "eye_right",
"pivot": [-1.9, 44.2, -8.07],
"cubes": [
{"origin": [-3.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [76, 79], "mirror": true}
{"origin": [-3.4, 42.95, -8.07], "size": [3, 2, 1], "inflate": 0.02, "uv": [76, 88], "mirror": true}
]
},
{
@@ -266,23 +266,41 @@
]
},
{
"name": "toe_back_left",
"name": "toe_back_inner_left",
"parent": "foot_left",
"pivot": [1.25, 1.92365, 1.01519],
"pivot": [0.75, 1.92365, 1.01519],
"rotation": [0, -12.5, 0],
"cubes": [
{"origin": [1.25, -0.07635, 1.01519], "size": [0, 1, 1], "inflate": 0.01, "uv": [0, 18]}
{"origin": [0.75, -0.07635, 1.01519], "size": [0, 1, 1], "inflate": 0.01, "uv": [0, 18]}
]
},
{
"name": "claw_back_left",
"parent": "toe_back_left",
"pivot": [1.25, 1.92365, 2.01519],
"name": "toe_back_inner_left2",
"parent": "toe_back_inner_left",
"pivot": [0.75, 1.92365, 2.01519],
"cubes": [
{"origin": [1.25, -0.07635, 2.01519], "size": [0, 1, 1], "uv": [4, 14]}
{"origin": [0.75, -0.07635, 2.01519], "size": [0, 1, 1], "uv": [4, 14]}
]
},
{
"name": "toe_inner_left",
"name": "toe_back_outer_left",
"parent": "foot_left",
"pivot": [1.75, 1.92365, 1.01519],
"rotation": [0, 12.5, 0],
"cubes": [
{"origin": [1.75, -0.07635, 1.01519], "size": [0, 1, 1], "inflate": 0.01, "uv": [0, 18]}
]
},
{
"name": "toe_back_outer_left2",
"parent": "toe_back_outer_left",
"pivot": [1.75, 1.92365, 2.01519],
"cubes": [
{"origin": [1.75, -0.07635, 2.01519], "size": [0, 1, 1], "uv": [4, 14]}
]
},
{
"name": "toe_front_inner_left",
"parent": "foot_left",
"pivot": [1.25, 1.92365, -0.98481],
"rotation": [0, 12.5, 0],
@@ -291,15 +309,15 @@
]
},
{
"name": "claw_inner_left",
"parent": "toe_inner_left",
"name": "toe_front_inner_left2",
"parent": "toe_front_inner_left",
"pivot": [0.75, 1.92365, -1.98481],
"cubes": [
{"origin": [0.75, -0.07635, -2.98481], "size": [0, 1, 1], "uv": [4, 13]}
]
},
{
"name": "toe_outer_left",
"name": "toe_front_outer_left",
"parent": "foot_left",
"pivot": [1.25, 1.92365, -0.98481],
"rotation": [0, -12.5, 0],
@@ -308,8 +326,8 @@
]
},
{
"name": "claw_outer_left",
"parent": "toe_outer_left",
"name": "toe_front_outer_left2",
"parent": "toe_front_outer_left",
"pivot": [1.75, 1.92365, -1.98481],
"cubes": [
{"origin": [1.75, -0.07635, -2.98481], "size": [0, 1, 1], "uv": [4, 5]}
@@ -343,23 +361,41 @@
]
},
{
"name": "toe_back_right",
"name": "toe_back_inner_right",
"parent": "foot_right",
"pivot": [-1.25, 1.92365, 1.01519],
"pivot": [-0.75, 1.92365, 1.01519],
"rotation": [0, 12.5, 0],
"cubes": [
{"origin": [-1.25, -0.07635, 1.01519], "size": [0, 1, 1], "inflate": 0.01, "uv": [0, 5]}
{"origin": [-0.75, -0.07635, 1.01519], "size": [0, 1, 1], "inflate": 0.01, "uv": [0, 5]}
]
},
{
"name": "claw_back_right",
"parent": "toe_back_right",
"pivot": [-1.25, 1.92365, 2.01519],
"name": "toe_back_inner_right2",
"parent": "toe_back_inner_right",
"pivot": [-0.75, 1.92365, 2.01519],
"cubes": [
{"origin": [-1.25, -0.07635, 2.01519], "size": [0, 1, 1], "uv": [4, 4]}
{"origin": [-0.75, -0.07635, 2.01519], "size": [0, 1, 1], "uv": [4, 4]}
]
},
{
"name": "toe_inner_right",
"name": "toe_back_inner_right3",
"parent": "foot_right",
"pivot": [-1.75, 1.92365, 1.01519],
"rotation": [0, -12.5, 0],
"cubes": [
{"origin": [-1.75, -0.07635, 1.01519], "size": [0, 1, 1], "inflate": 0.01, "uv": [0, 5]}
]
},
{
"name": "toe_back_inner_right4",
"parent": "toe_back_inner_right3",
"pivot": [-1.75, 1.92365, 2.01519],
"cubes": [
{"origin": [-1.75, -0.07635, 2.01519], "size": [0, 1, 1], "uv": [4, 4]}
]
},
{
"name": "toe_front_inner_right",
"parent": "foot_right",
"pivot": [-1.25, 1.92365, -0.98481],
"rotation": [0, -12.5, 0],
@@ -368,15 +404,15 @@
]
},
{
"name": "claw_inner_right",
"parent": "toe_inner_right",
"name": "toe_front_inner_right2",
"parent": "toe_front_inner_right",
"pivot": [-0.75, 1.92365, -1.98481],
"cubes": [
{"origin": [-0.75, -0.07635, -2.98481], "size": [0, 1, 1], "uv": [4, 0]}
]
},
{
"name": "toe_outer_right",
"name": "toe_front_outer_right",
"parent": "foot_right",
"pivot": [-1.25, 1.92365, -0.98481],
"rotation": [0, 12.5, 0],
@@ -385,8 +421,8 @@
]
},
{
"name": "claw_outer_right",
"parent": "toe_outer_right",
"name": "toe_front_outer_right2",
"parent": "toe_front_outer_right",
"pivot": [-1.75, 1.92365, -1.98481],
"cubes": [
{"origin": [-1.75, -0.07635, -2.98481], "size": [0, 1, 1], "uv": [0, 0]}
@@ -1,8 +1,8 @@
{
"portraitScale": 2.1,
"portraitTranslation": [ -0.55, -1.55, 0 ],
"profileScale": 0.80,
"profileTranslation": [ 0, 0.55, 0 ],
"profileScale": 0.72,
"profileTranslation": [ 0, 0.63, 0 ],
"rootBone": "caterpie",
"animations": {
"cry": "q.bedrock_stateful('caterpie', 'cry')",
Loaded 100 of 662 files, more files were not shown because too many files have changed in this diff. Show more