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
Polymeta f9dd1448e5 Merge branch 'item-2072' into 'main'
Added tab completion for the alpha property and three missing property aliases

Closes #2072

See merge request cable-mc/cobblemon!2388
2026-10-18 22:33:30 +00:00
Gitoido ecf5d57ad2 chore: resolve conflicts
# Conflicts:
#   common/src/main/kotlin/com/cobblemon/mod/common/pokemon/properties/PropertiesCompletionProvider.kt
2026-10-18 22:16:27 +00:00
Polymeta 8663888abc Merge branch 'item-2013' into 'main'
Fixed GUI Pokémon cries playing on the Master category instead of Friendly Creatures

Closes #2013

See merge request cable-mc/cobblemon!2389
2026-10-18 22:14:04 +00:00
Polymeta f452cd9a05 Merge branch 'fix/ride-interact-checks' into 'main'
fix: stricter riding checks on server side

Closes #2124

See merge request cable-mc/cobblemon!2499
2026-10-18 22:12:19 +00:00
Kiritiop 4fe9cd7fd6 Fixed GUI pokemon cries using the Master volume slider instead of Friendly Creatures, added a new file to prevent code duplication. Credit to Gitoido 2026-10-18 13:19:07 -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
Gitoido Okaski 2c1d11ed11 chore: remove duplicate check that was accounted for earlier 2026-10-18 15:29:35 +03:00
Gitoido Okaski b88adf0479 fix: stricter riding checks on server side 2026-10-18 15:21:09 +03:00
Polymeta b1066671f5 Merge branch 'quick-fix' into 'main'
Flowerbed Clearning jigsaw fix, dripstone oasis jigsaw removal

See merge request cable-mc/cobblemon!2497
2026-10-18 12:09:48 +00:00
Polymeta 387e85d4ad Merge branch 'fix/preset-conditions-mergers' into 'main'
fix: Some spawn conditions merge minHeight/Depth data into max instead

Closes #2111

See merge request cable-mc/cobblemon!2498
2026-10-18 12:07:55 +00: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
Gitoido Okaski 77ab58d823 fix: spawn condition mergers writing minHeight/depth into maxHeight/depth instead 2026-10-18 14:14:28 +03:00
Slippy 1a9e736d8b Flowerbed Clearning jigsaw fix, dripstone oasis jigsaw removal 2026-10-18 00:11:56 -05: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
Hiroku aa39c1906e Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2495
2026-10-17 23:48:54 +00:00
ewwww 2e460e2ca2 fixed floating frogadier 2026-10-17 19:43:44 -04:00
Polymeta a07ff7f442 Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2494
2026-10-17 23:37:18 +00:00
Polymeta 5374569ea1 [skip-ci] tidy up changelog
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-18 00:15:37 +01:00
Polymeta af9c1196bf youre meant to be looking the other way
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-18 00:08:51 +01:00
Polymeta 09d5543469 Merge branch 'fix/haters-gonna-hate-properly' into 'main'
fix: Anger should be directed at proper target

Closes #2112

See merge request cable-mc/cobblemon!2492
2026-10-17 23:03:41 +00:00
ewwww 49e4dd0298 helioptile & heliolisk cries 2026-10-17 19:01:46 -04:00
Polymeta c8e408c50a actually copy components when building up evolution displays
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-17 23:57:05 +01:00
Gitoido Okaski e8a0467643 chore: move coercion at class variable level 2026-10-18 01:17:07 +03:00
Gitoido Okaski 2dd0b66b83 chore: remove duplicate i18n key 2026-10-18 01:04:29 +03:00
Gitoido Okaski 68374017e1 chore: changelog 2026-10-18 01:04:29 +03:00
ewwww 120520f51d vikavolt animations + cries for grubbin, charjabug, & vikavolt 2026-10-17 18:01:25 -04:00
Gitoido Okaski e8dea9773a feat: make sure zangoose and zeviper 100% hate each other 2026-10-18 00:59:40 +03:00
Gitoido Okaski 7b31e6bcc4 feat: make sure hate task uses new values from config and gamerule 2026-10-18 00:59:10 +03:00
Gitoido Okaski c6ffca639f feat: pokemon predation chance config i18n 2026-10-18 00:58:40 +03:00
Gitoido Okaski c491a1f7ca feat: introduce pokemon predation chance config 2026-10-18 00:56:06 +03:00
Gitoido Okaski 0b333b743d feat: introduce pokemon predation gamerule 2026-10-18 00:55:54 +03:00
ewwww 95e79865e5 updated posers for fennekin & braixen 2026-10-17 16:24:40 -04:00
Gitoido Okaski 0e2860cd90 chore: import cleanup skip_ci 2026-10-17 23:14:08 +03:00
Gitoido Okaski 3259bf032c chore: changelog skip_ci 2026-10-17 23:10:15 +03:00
Gitoido Okaski 116205cdf1 fix: Anger should be directed at proper target 2026-10-17 20:07:18 +00:00
ewwww 43277d18dc Merge remote-tracking branch 'origin/implement_pokemon' into implement_pokemon 2026-10-17 13:37:05 -04:00
Polymeta 86153419ee Merge branch 'implement_pokemon' into 'main'
implement_pokemon

See merge request cable-mc/cobblemon!2480
2026-10-17 17:36:00 +00:00
Gitoido Okaski 2b4d9c451c fix: Various poser/animation fixes (Wave 3.4.1) 2026-10-17 20:07:17 +03:00
Gitoido Okaski 7f0e2fa9d8 fix: Various poser/animation fixes (Wave 3.3) 2026-10-17 19:43:00 +03:00
Gitoido Okaski e165095bd5 fix: Various poser/animation fixes (Wave 3.2.1) 2026-10-17 19:38:06 +03:00
Gitoido Okaski b8708d6e10 fix: Various poser/animation fixes (Wave 3.2) 2026-10-17 19:36:25 +03:00
Kenji_64 ef536390ca Various poser/animation fixes (Wave 3.1). The rats make me crazy. 2026-10-17 18:27:28 +02:00
ewwww 28befab173 cries for combee & vespiquen 2026-10-17 12:22:20 -04:00
ewwww c142439d5b cries for wurmple, silcoon, cascoon. beautifly, & dustox 2026-10-17 12:21:49 -04:00
Jerzykv 1ca7335654 Various poser/animation fixes (Wave 3) 2026-10-17 18:18:39 +02:00
ewwww 9197d9cdcd cries for runerigus 2026-10-17 11:52:04 -04:00
ewwww f75813e64d cries for dusknoir 2026-10-17 11:45:44 -04:00
ewwww 1f617c76a4 cries for torkoal 2026-10-17 11:45:20 -04:00
ewwww ebf573aa7a cries for surskit & masquerain 2026-10-17 11:44:29 -04:00
Kenji_64 75363da470 Various poser/animation fixes (Wave 2.5) 2026-10-17 17:42:04 +02:00
ewwww cce4b932cc cries for hoppip, skiploom, & jumpluff 2026-10-17 11:41:45 -04:00
Kenji_64 0973e1ced0 Various poser/animation fixes (Wave 2) 2026-10-17 17:31:30 +02:00
Kenji_64 e7b5c84024 Various poser/animation fixes 2026-10-17 13:43:58 +02:00
ewwww 4001cc2a89 vikavolt animations + cries for grubbin, charjabug, & vikavolt 2026-10-17 06:08:31 -04:00
Polymeta 6e57d77d10 Merge branch 'More-Structure-Fixes' into 'main'
More structure fixes

See merge request cable-mc/cobblemon!2491
2026-10-16 22:54:46 +00:00
Slippy 2206d96a6e Lots of fixing 2026-10-16 14:26:55 -05:00
Slippy 33f05858f1 structure fixes for .3 2026-10-16 13:34:04 -05: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
ewwww a4f204a5a3 changelog 2026-10-16 06:06:11 -04:00
ewwww 31b6be2164 applin & flapple cry updates + cries for appletun & dipplin 2026-10-16 06:06:00 -04:00
ewwww 0dbf73067b houndour & houndoom cries 2026-10-16 06:03:48 -04:00
ewwww 775703e1f5 starly, staravia, and staraptor cries 2026-10-16 06:03:20 -04:00
Polymeta bcdfef4886 Merge branch 'lights-out' into 'main'
Bye Bye Magmarizer Glow

See merge request cable-mc/cobblemon!2490
2026-10-16 06:49:58 +00:00
dhi_holo a19adb463b Bye Bye Magmarizer Glow
- Removed Dynamic Lights data from the Magmarizer item for consistency with the block
2026-10-16 16:32:05 +10: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
Polymeta 5004edbbb6 Merge branch 'fix/party-overlay-hide-nf' into 'main'
fix: party overlay not hiding when player opens the pokedex scanner

Closes #2071

See merge request cable-mc/cobblemon!2467
2026-10-15 15:37:49 +00:00
Gitoido Okaski d8bdd6329a fix: tidy-up even more 2026-10-15 17:57:42 +03:00
Gitoido Okaski 82d56637a0 chore: method signature name 2026-10-15 17:07:28 +03:00
Gitoido Okaski 7232a43816 refactor: move and enqueue client event registration to separate fn, homogenizing behavior with common side registration 2026-10-15 17:05:50 +03:00
Gitoido Okaski 098eb7d14b chore: tidy up changelog skip-ci 2026-10-15 16:22:30 +03:00
Gitoido Okaski 835b70ed48 feat: AppleSkin compat for dex scanner mode 2026-10-15 13:20:53 +00:00
Gitoido 9d828a86fb fix: party overlay not hiding when player opens the pokedex scanner 2026-10-15 13:20:53 +00:00
Polymeta 910551ee70 Merge branch 'fix/berry-pots' into 'main'
fix: flower pots require some tending on neoforge

Closes #2126

See merge request cable-mc/cobblemon!2486
2026-10-15 07:17:23 +00:00
ewwww 9beee548d4 Merge branch 'main' into implement_pokemon 2026-10-15 01:30:50 -04: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
Polymeta 6f3894f27b fix playermolang sometimes failing when they die
Signed-off-by: Polymeta <4726322-polymeta@users.noreply.gitlab.com>
2026-10-14 13:33:40 +01:00
Polymeta bbc9a5e62e Merge branch 'campfire-book-lies' into 'main'
Recipe Book Lies No More

See merge request cable-mc/cobblemon!2487
2026-10-14 11:32:52 +00:00
Gitoido Okaski 94765da04a chore: small tidy-up 2026-10-14 14:11:30 +03:00
dhi_holo 8caa82f123 Recipe Book Lies No More
Changed the icon for the misc category
2026-10-14 18:42:58 +10: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 Okaski 131ef31492 fix: tend to the pots registration on neoforge 2026-10-13 03:38:13 +03:00
Gitoido Okaski b6b067b1d3 chore: changelog skip-ci 2026-10-13 03:35:28 +03:00
RedGenesectNinja 546f10eca7 Gave Audino its alpha eyes back 2026-10-12 19:19:05 -05: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
Kiritiop fb4aaed220 Edit CHANGELOG.md 2026-10-11 08:57:22 +00:00
Kiritiop c5e1b37ead Manual rebase 2026-10-11 08:53:30 +00:00
Kiritiop 2be9d303ea Added tab completion for the alpha property and three missing property aliases
Closes #2072
2026-10-11 08:53:30 +00:00
Kiritiop 99ac6d213a Added tab completion for the alpha property and three missing property aliases
Closes #2072
2026-10-11 08:53:30 +00:00
ewwww 6f4aae8b7d grubbin, charjabug, and vikavolt are marked as implemented 2026-10-10 23:33:06 -04:00
ewwww 01e5e74e64 snover and abomasnow are marked as implemented 2026-10-10 23:32:49 -04:00
ewwww b6565a5dee greavard and houndstone are marked as implemented 2026-10-10 23:29:38 -04:00
855 changed files with 33480 additions and 4689 deletions

No files matched your search

+4 -35
View File
@@ -1,46 +1,15 @@
# Changelog
## [1.8.3 (MONTH xth, 2026)](#1-8-3)
## [1.8.x (MONTH xth, 2026)](#1-8-x)
### Additions
### Pokémon Added
#### Generation 4
#### Generation 5
#### Generation 6
#### Generation 7
#### Generation 8
#### Generation 9
### Model updates for the following Pokémon
### Animation updates for the following Pokémon
### Changes
### 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 pokemon has several pokedex 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 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
### Developer
- Bumped NeoForge to `21.1.195` to get access to MixinExtras `0.5` and the expressions annotation
### 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
### 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();
}
}
@@ -260,6 +260,9 @@ object Cobblemon {
RequestManager.onLogoff(it.player)
serverPlayerStructs.remove(it.player.uuid)
}
PlatformEvents.PLAYER_RESPAWN.subscribe {
serverPlayerStructs.remove(it.player.uuid)
}
PlatformEvents.PLAYER_DEATH.subscribe {
PCLinkManager.removeLink(it.player.uuid)
battleRegistry.getBattleByParticipatingPlayer(it.player)?.stop()
@@ -845,54 +845,35 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
@JvmField
val PEP_UP_FLOWER = this.create("pep_up_flower", FlowerBlock(MobEffects.LEVITATION, 10F, BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).pushReaction(PushReaction.DESTROY)))
@JvmField
val POTTED_PEP_UP_FLOWER = this.create("potted_pep_up_flower", BlocksInvoker.createFlowerPotBlock(PEP_UP_FLOWER))
@JvmField
val HEARTY_GRAINS = this.create("hearty_grains", HeartyGrainsBlock(BlockBehaviour.Properties.of().pushReaction(PushReaction.DESTROY).ignitedByLava().mapColor(MapColor.PLANT).noCollission().randomTicks().instabreak()))
@JvmField
val GALARICA_NUT_BUSH = this.create("galarica_nut_bush", NutBushBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).randomTicks().noCollission().sound(SoundType.SWEET_BERRY_BUSH).pushReaction(PushReaction.DESTROY)))
@JvmField
val POTTED_RED_APRICORN_SAPLING = this.create("potted_red_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(RED_APRICORN_SAPLING))
@JvmField
val POTTED_YELLOW_APRICORN_SAPLING = this.create("potted_yellow_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(YELLOW_APRICORN_SAPLING))
@JvmField
val POTTED_GREEN_APRICORN_SAPLING = this.create("potted_green_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(GREEN_APRICORN_SAPLING))
@JvmField
val POTTED_BLUE_APRICORN_SAPLING = this.create("potted_blue_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLUE_APRICORN_SAPLING))
@JvmField
val POTTED_PINK_APRICORN_SAPLING = this.create("potted_pink_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(PINK_APRICORN_SAPLING))
@JvmField
val POTTED_BLACK_APRICORN_SAPLING = this.create("potted_black_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(BLACK_APRICORN_SAPLING))
@JvmField
val POTTED_WHITE_APRICORN_SAPLING = this.create("potted_white_apricorn_sapling", BlocksInvoker.createFlowerPotBlock(WHITE_APRICORN_SAPLING))
@JvmField
val POTTED_SACCHARINE_SAPLING = this.create("potted_saccharine_sapling", BlocksInvoker.createFlowerPotBlock(SACCHARINE_SAPLING))
@JvmField
val RED_MINT_LEAF_LITTER = this.mintLitter("red_mint_leaf_litter", MapColor.COLOR_RED)
@JvmField
val POTTED_RED_MINT = this.pottedPlant("potted_red_mint", RED_MINT_LEAF_LITTER)
val POTTED_RED_MINT = this.flowerPot("potted_red_mint", RED_MINT_LEAF_LITTER)
@JvmField
val BLUE_MINT_LEAF_LITTER = this.mintLitter("blue_mint_leaf_litter", MapColor.COLOR_BLUE)
@JvmField
val POTTED_BLUE_MINT = this.pottedPlant("potted_blue_mint", BLUE_MINT_LEAF_LITTER)
val POTTED_BLUE_MINT = this.flowerPot("potted_blue_mint", BLUE_MINT_LEAF_LITTER)
@JvmField
val CYAN_MINT_LEAF_LITTER = this.mintLitter("cyan_mint_leaf_litter", MapColor.COLOR_CYAN)
@JvmField
val POTTED_CYAN_MINT = this.pottedPlant("potted_cyan_mint", CYAN_MINT_LEAF_LITTER)
val POTTED_CYAN_MINT = this.flowerPot("potted_cyan_mint", CYAN_MINT_LEAF_LITTER)
@JvmField
val PINK_MINT_LEAF_LITTER = this.mintLitter("pink_mint_leaf_litter", MapColor.COLOR_PINK)
@JvmField
val POTTED_PINK_MINT = this.pottedPlant("potted_pink_mint", PINK_MINT_LEAF_LITTER)
val POTTED_PINK_MINT = this.flowerPot("potted_pink_mint", PINK_MINT_LEAF_LITTER)
@JvmField
val GREEN_MINT_LEAF_LITTER = this.mintLitter("green_mint_leaf_litter", MapColor.COLOR_GREEN)
@JvmField
val POTTED_GREEN_MINT = this.pottedPlant("potted_green_mint", GREEN_MINT_LEAF_LITTER)
val POTTED_GREEN_MINT = this.flowerPot("potted_green_mint", GREEN_MINT_LEAF_LITTER)
@JvmField
val WHITE_MINT_LEAF_LITTER = this.mintLitter("white_mint_leaf_litter", MapColor.SNOW)
@JvmField
val POTTED_WHITE_MINT = this.pottedPlant("potted_white_mint", WHITE_MINT_LEAF_LITTER)
val POTTED_WHITE_MINT = this.flowerPot("potted_white_mint", WHITE_MINT_LEAF_LITTER)
@JvmField
val LOBAILA = this.create("lobaila", LobailaFlowerBlock(BlockBehaviour.Properties.of().mapColor(MapColor.PLANT).noCollission().instabreak().sound(SoundType.GRASS).offsetType(BlockBehaviour.OffsetType.XZ).ignitedByLava().pushReaction(PushReaction.DESTROY)))
@@ -1104,145 +1085,163 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
//val BINDING_SOIL = this.create("binding_soil", BindingSoilBlock(BlockBehaviour.Properties.of()))
@JvmField
val POTTED_AGUAV_BERRY = this.create("potted_aguav_berry", BlocksInvoker.createFlowerPotBlock(AGUAV_BERRY))
val POTTED_AGUAV_BERRY = this.flowerPot("potted_aguav_berry", AGUAV_BERRY)
@JvmField
val POTTED_APICOT_BERRY = this.create("potted_apicot_berry", BlocksInvoker.createFlowerPotBlock(APICOT_BERRY))
val POTTED_APICOT_BERRY = this.flowerPot("potted_apicot_berry", APICOT_BERRY)
@JvmField
val POTTED_ASPEAR_BERRY = this.create("potted_aspear_berry", BlocksInvoker.createFlowerPotBlock(ASPEAR_BERRY))
val POTTED_ASPEAR_BERRY = this.flowerPot("potted_aspear_berry", ASPEAR_BERRY)
@JvmField
val POTTED_BABIRI_BERRY = this.create("potted_babiri_berry", BlocksInvoker.createFlowerPotBlock(BABIRI_BERRY))
val POTTED_BABIRI_BERRY = this.flowerPot("potted_babiri_berry", BABIRI_BERRY)
@JvmField
val POTTED_BELUE_BERRY = this.create("potted_belue_berry", BlocksInvoker.createFlowerPotBlock(BELUE_BERRY))
val POTTED_BELUE_BERRY = this.flowerPot("potted_belue_berry", BELUE_BERRY)
@JvmField
val POTTED_BLUK_BERRY = this.create("potted_bluk_berry", BlocksInvoker.createFlowerPotBlock(BLUK_BERRY))
val POTTED_BLUK_BERRY = this.flowerPot("potted_bluk_berry", BLUK_BERRY)
@JvmField
val POTTED_CHARTI_BERRY = this.create("potted_charti_berry", BlocksInvoker.createFlowerPotBlock(CHARTI_BERRY))
val POTTED_CHARTI_BERRY = this.flowerPot("potted_charti_berry", CHARTI_BERRY)
@JvmField
val POTTED_CHERI_BERRY = this.create("potted_cheri_berry", BlocksInvoker.createFlowerPotBlock(CHERI_BERRY))
val POTTED_CHERI_BERRY = this.flowerPot("potted_cheri_berry", CHERI_BERRY)
@JvmField
val POTTED_CHESTO_BERRY = this.create("potted_chesto_berry", BlocksInvoker.createFlowerPotBlock(CHESTO_BERRY))
val POTTED_CHESTO_BERRY = this.flowerPot("potted_chesto_berry", CHESTO_BERRY)
@JvmField
val POTTED_CHILAN_BERRY = this.create("potted_chilan_berry", BlocksInvoker.createFlowerPotBlock(CHILAN_BERRY))
val POTTED_CHILAN_BERRY = this.flowerPot("potted_chilan_berry", CHILAN_BERRY)
@JvmField
val POTTED_CHOPLE_BERRY = this.create("potted_chople_berry", BlocksInvoker.createFlowerPotBlock(CHOPLE_BERRY))
val POTTED_CHOPLE_BERRY = this.flowerPot("potted_chople_berry", CHOPLE_BERRY)
@JvmField
val POTTED_COBA_BERRY = this.create("potted_coba_berry", BlocksInvoker.createFlowerPotBlock(COBA_BERRY))
val POTTED_COBA_BERRY = this.flowerPot("potted_coba_berry", COBA_BERRY)
@JvmField
val POTTED_COLBUR_BERRY = this.create("potted_colbur_berry", BlocksInvoker.createFlowerPotBlock(COLBUR_BERRY))
val POTTED_COLBUR_BERRY = this.flowerPot("potted_colbur_berry", COLBUR_BERRY)
@JvmField
val POTTED_CORNN_BERRY = this.create("potted_cornn_berry", BlocksInvoker.createFlowerPotBlock(CORNN_BERRY))
val POTTED_CORNN_BERRY = this.flowerPot("potted_cornn_berry", CORNN_BERRY)
@JvmField
val POTTED_CUSTAP_BERRY = this.create("potted_custap_berry", BlocksInvoker.createFlowerPotBlock(CUSTAP_BERRY))
val POTTED_CUSTAP_BERRY = this.flowerPot("potted_custap_berry", CUSTAP_BERRY)
@JvmField
val POTTED_DURIN_BERRY = this.create("potted_durin_berry", BlocksInvoker.createFlowerPotBlock(DURIN_BERRY))
val POTTED_DURIN_BERRY = this.flowerPot("potted_durin_berry", DURIN_BERRY)
@JvmField
val POTTED_EGGANT_BERRY = this.create("potted_eggant_berry", BlocksInvoker.createFlowerPotBlock(EGGANT_BERRY))
val POTTED_EGGANT_BERRY = this.flowerPot("potted_eggant_berry", EGGANT_BERRY)
@JvmField
val POTTED_ENIGMA_BERRY = this.create("potted_enigma_berry", BlocksInvoker.createFlowerPotBlock(ENIGMA_BERRY))
val POTTED_ENIGMA_BERRY = this.flowerPot("potted_enigma_berry", ENIGMA_BERRY)
@JvmField
val POTTED_FIGY_BERRY = this.create("potted_figy_berry", BlocksInvoker.createFlowerPotBlock(FIGY_BERRY))
val POTTED_FIGY_BERRY = this.flowerPot("potted_figy_berry", FIGY_BERRY)
@JvmField
val POTTED_GANLON_BERRY = this.create("potted_ganlon_berry", BlocksInvoker.createFlowerPotBlock(GANLON_BERRY))
val POTTED_GANLON_BERRY = this.flowerPot("potted_ganlon_berry", GANLON_BERRY)
@JvmField
val POTTED_GREPA_BERRY = this.create("potted_grepa_berry", BlocksInvoker.createFlowerPotBlock(GREPA_BERRY))
val POTTED_GREPA_BERRY = this.flowerPot("potted_grepa_berry", GREPA_BERRY)
@JvmField
val POTTED_HABAN_BERRY = this.create("potted_haban_berry", BlocksInvoker.createFlowerPotBlock(HABAN_BERRY))
val POTTED_HABAN_BERRY = this.flowerPot("potted_haban_berry", HABAN_BERRY)
@JvmField
val POTTED_HONDEW_BERRY = this.create("potted_hondew_berry", BlocksInvoker.createFlowerPotBlock(HONDEW_BERRY))
val POTTED_HONDEW_BERRY = this.flowerPot("potted_hondew_berry", HONDEW_BERRY)
@JvmField
val POTTED_HOPO_BERRY = this.create("potted_hopo_berry", BlocksInvoker.createFlowerPotBlock(HOPO_BERRY))
val POTTED_HOPO_BERRY = this.flowerPot("potted_hopo_berry", HOPO_BERRY)
@JvmField
val POTTED_IAPAPA_BERRY = this.create("potted_iapapa_berry", BlocksInvoker.createFlowerPotBlock(IAPAPA_BERRY))
val POTTED_IAPAPA_BERRY = this.flowerPot("potted_iapapa_berry", IAPAPA_BERRY)
@JvmField
val POTTED_JABOCA_BERRY = this.create("potted_jaboca_berry", BlocksInvoker.createFlowerPotBlock(JABOCA_BERRY))
val POTTED_JABOCA_BERRY = this.flowerPot("potted_jaboca_berry", JABOCA_BERRY)
@JvmField
val POTTED_KASIB_BERRY = this.create("potted_kasib_berry", BlocksInvoker.createFlowerPotBlock(KASIB_BERRY))
val POTTED_KASIB_BERRY = this.flowerPot("potted_kasib_berry", KASIB_BERRY)
@JvmField
val POTTED_KEBIA_BERRY = this.create("potted_kebia_berry", BlocksInvoker.createFlowerPotBlock(KEBIA_BERRY))
val POTTED_KEBIA_BERRY = this.flowerPot("potted_kebia_berry", KEBIA_BERRY)
@JvmField
val POTTED_KEE_BERRY = this.create("potted_kee_berry", BlocksInvoker.createFlowerPotBlock(KEE_BERRY))
val POTTED_KEE_BERRY = this.flowerPot("potted_kee_berry", KEE_BERRY)
@JvmField
val POTTED_KELPSY_BERRY = this.create("potted_kelpsy_berry", BlocksInvoker.createFlowerPotBlock(KELPSY_BERRY))
val POTTED_KELPSY_BERRY = this.flowerPot("potted_kelpsy_berry", KELPSY_BERRY)
@JvmField
val POTTED_LANSAT_BERRY = this.create("potted_lansat_berry", BlocksInvoker.createFlowerPotBlock(LANSAT_BERRY))
val POTTED_LANSAT_BERRY = this.flowerPot("potted_lansat_berry", LANSAT_BERRY)
@JvmField
val POTTED_LEPPA_BERRY = this.create("potted_leppa_berry", BlocksInvoker.createFlowerPotBlock(LEPPA_BERRY))
val POTTED_LEPPA_BERRY = this.flowerPot("potted_leppa_berry", LEPPA_BERRY)
@JvmField
val POTTED_LIECHI_BERRY = this.create("potted_liechi_berry", BlocksInvoker.createFlowerPotBlock(LIECHI_BERRY))
val POTTED_LIECHI_BERRY = this.flowerPot("potted_liechi_berry", LIECHI_BERRY)
@JvmField
val POTTED_LUM_BERRY = this.create("potted_lum_berry", BlocksInvoker.createFlowerPotBlock(LUM_BERRY))
val POTTED_LUM_BERRY = this.flowerPot("potted_lum_berry", LUM_BERRY)
@JvmField
val POTTED_MAGO_BERRY = this.create("potted_mago_berry", BlocksInvoker.createFlowerPotBlock(MAGO_BERRY))
val POTTED_MAGO_BERRY = this.flowerPot("potted_mago_berry", MAGO_BERRY)
@JvmField
val POTTED_MAGOST_BERRY = this.create("potted_magost_berry", BlocksInvoker.createFlowerPotBlock(MAGOST_BERRY))
val POTTED_MAGOST_BERRY = this.flowerPot("potted_magost_berry", MAGOST_BERRY)
@JvmField
val POTTED_MARANGA_BERRY = this.create("potted_maranga_berry", BlocksInvoker.createFlowerPotBlock(MARANGA_BERRY))
val POTTED_MARANGA_BERRY = this.flowerPot("potted_maranga_berry", MARANGA_BERRY)
@JvmField
val POTTED_MICLE_BERRY = this.create("potted_micle_berry", BlocksInvoker.createFlowerPotBlock(MICLE_BERRY))
val POTTED_MICLE_BERRY = this.flowerPot("potted_micle_berry", MICLE_BERRY)
@JvmField
val POTTED_NANAB_BERRY = this.create("potted_nanab_berry", BlocksInvoker.createFlowerPotBlock(NANAB_BERRY))
val POTTED_NANAB_BERRY = this.flowerPot("potted_nanab_berry", NANAB_BERRY)
@JvmField
val POTTED_NOMEL_BERRY = this.create("potted_nomel_berry", BlocksInvoker.createFlowerPotBlock(NOMEL_BERRY))
val POTTED_NOMEL_BERRY = this.flowerPot("potted_nomel_berry", NOMEL_BERRY)
@JvmField
val POTTED_OCCA_BERRY = this.create("potted_occa_berry", BlocksInvoker.createFlowerPotBlock(OCCA_BERRY))
val POTTED_OCCA_BERRY = this.flowerPot("potted_occa_berry", OCCA_BERRY)
@JvmField
val POTTED_ORAN_BERRY = this.create("potted_oran_berry", BlocksInvoker.createFlowerPotBlock(ORAN_BERRY))
val POTTED_ORAN_BERRY = this.flowerPot("potted_oran_berry", ORAN_BERRY)
@JvmField
val POTTED_PAMTRE_BERRY = this.create("potted_pamtre_berry", BlocksInvoker.createFlowerPotBlock(PAMTRE_BERRY))
val POTTED_PAMTRE_BERRY = this.flowerPot("potted_pamtre_berry", PAMTRE_BERRY)
@JvmField
val POTTED_PASSHO_BERRY = this.create("potted_passho_berry", BlocksInvoker.createFlowerPotBlock(PASSHO_BERRY))
val POTTED_PASSHO_BERRY = this.flowerPot("potted_passho_berry", PASSHO_BERRY)
@JvmField
val POTTED_PAYAPA_BERRY = this.create("potted_payapa_berry", BlocksInvoker.createFlowerPotBlock(PAYAPA_BERRY))
val POTTED_PAYAPA_BERRY = this.flowerPot("potted_payapa_berry", PAYAPA_BERRY)
@JvmField
val POTTED_PECHA_BERRY = this.create("potted_pecha_berry", BlocksInvoker.createFlowerPotBlock(PECHA_BERRY))
val POTTED_PECHA_BERRY = this.flowerPot("potted_pecha_berry", PECHA_BERRY)
@JvmField
val POTTED_PERSIM_BERRY = this.create("potted_persim_berry", BlocksInvoker.createFlowerPotBlock(PERSIM_BERRY))
val POTTED_PERSIM_BERRY = this.flowerPot("potted_persim_berry", PERSIM_BERRY)
@JvmField
val POTTED_PETAYA_BERRY = this.create("potted_petaya_berry", BlocksInvoker.createFlowerPotBlock(PETAYA_BERRY))
val POTTED_PETAYA_BERRY = this.flowerPot("potted_petaya_berry", PETAYA_BERRY)
@JvmField
val POTTED_PINAP_BERRY = this.create("potted_pinap_berry", BlocksInvoker.createFlowerPotBlock(PINAP_BERRY))
val POTTED_PINAP_BERRY = this.flowerPot("potted_pinap_berry", PINAP_BERRY)
@JvmField
val POTTED_POMEG_BERRY = this.create("potted_pomeg_berry", BlocksInvoker.createFlowerPotBlock(POMEG_BERRY))
val POTTED_POMEG_BERRY = this.flowerPot("potted_pomeg_berry", POMEG_BERRY)
@JvmField
val POTTED_QUALOT_BERRY = this.create("potted_qualot_berry", BlocksInvoker.createFlowerPotBlock(QUALOT_BERRY))
val POTTED_QUALOT_BERRY = this.flowerPot("potted_qualot_berry", QUALOT_BERRY)
@JvmField
val POTTED_RABUTA_BERRY = this.create("potted_rabuta_berry", BlocksInvoker.createFlowerPotBlock(RABUTA_BERRY))
val POTTED_RABUTA_BERRY = this.flowerPot("potted_rabuta_berry", RABUTA_BERRY)
@JvmField
val POTTED_RAWST_BERRY = this.create("potted_rawst_berry", BlocksInvoker.createFlowerPotBlock(RAWST_BERRY))
val POTTED_RAWST_BERRY = this.flowerPot("potted_rawst_berry", RAWST_BERRY)
@JvmField
val POTTED_RAZZ_BERRY = this.create("potted_razz_berry", BlocksInvoker.createFlowerPotBlock(RAZZ_BERRY))
val POTTED_RAZZ_BERRY = this.flowerPot("potted_razz_berry", RAZZ_BERRY)
@JvmField
val POTTED_RINDO_BERRY = this.create("potted_rindo_berry", BlocksInvoker.createFlowerPotBlock(RINDO_BERRY))
val POTTED_RINDO_BERRY = this.flowerPot("potted_rindo_berry", RINDO_BERRY)
@JvmField
val POTTED_ROSELI_BERRY = this.create("potted_roseli_berry", BlocksInvoker.createFlowerPotBlock(ROSELI_BERRY))
val POTTED_ROSELI_BERRY = this.flowerPot("potted_roseli_berry", ROSELI_BERRY)
@JvmField
val POTTED_ROWAP_BERRY = this.create("potted_rowap_berry", BlocksInvoker.createFlowerPotBlock(ROWAP_BERRY))
val POTTED_ROWAP_BERRY = this.flowerPot("potted_rowap_berry", ROWAP_BERRY)
@JvmField
val POTTED_SALAC_BERRY = this.create("potted_salac_berry", BlocksInvoker.createFlowerPotBlock(SALAC_BERRY))
val POTTED_SALAC_BERRY = this.flowerPot("potted_salac_berry", SALAC_BERRY)
@JvmField
val POTTED_SHUCA_BERRY = this.create("potted_shuca_berry", BlocksInvoker.createFlowerPotBlock(SHUCA_BERRY))
val POTTED_SHUCA_BERRY = this.flowerPot("potted_shuca_berry", SHUCA_BERRY)
@JvmField
val POTTED_SITRUS_BERRY = this.create("potted_sitrus_berry", BlocksInvoker.createFlowerPotBlock(SITRUS_BERRY))
val POTTED_SITRUS_BERRY = this.flowerPot("potted_sitrus_berry", SITRUS_BERRY)
@JvmField
val POTTED_SPELON_BERRY = this.create("potted_spelon_berry", BlocksInvoker.createFlowerPotBlock(SPELON_BERRY))
val POTTED_SPELON_BERRY = this.flowerPot("potted_spelon_berry", SPELON_BERRY)
@JvmField
val POTTED_STARF_BERRY = this.create("potted_starf_berry", BlocksInvoker.createFlowerPotBlock(STARF_BERRY))
val POTTED_STARF_BERRY = this.flowerPot("potted_starf_berry", STARF_BERRY)
@JvmField
val POTTED_TAMATO_BERRY = this.create("potted_tamato_berry", BlocksInvoker.createFlowerPotBlock(TAMATO_BERRY))
val POTTED_TAMATO_BERRY = this.flowerPot("potted_tamato_berry", TAMATO_BERRY)
@JvmField
val POTTED_TANGA_BERRY = this.create("potted_tanga_berry", BlocksInvoker.createFlowerPotBlock(TANGA_BERRY))
val POTTED_TANGA_BERRY = this.flowerPot("potted_tanga_berry", TANGA_BERRY)
@JvmField
val POTTED_TOUGA_BERRY = this.create("potted_touga_berry", BlocksInvoker.createFlowerPotBlock(TOUGA_BERRY))
val POTTED_TOUGA_BERRY = this.flowerPot("potted_touga_berry", TOUGA_BERRY)
@JvmField
val POTTED_WACAN_BERRY = this.create("potted_wacan_berry", BlocksInvoker.createFlowerPotBlock(WACAN_BERRY))
val POTTED_WACAN_BERRY = this.flowerPot("potted_wacan_berry", WACAN_BERRY)
@JvmField
val POTTED_WATMEL_BERRY = this.create("potted_watmel_berry", BlocksInvoker.createFlowerPotBlock(WATMEL_BERRY))
val POTTED_WATMEL_BERRY = this.flowerPot("potted_watmel_berry", WATMEL_BERRY)
@JvmField
val POTTED_WEPEAR_BERRY = this.create("potted_wepear_berry", BlocksInvoker.createFlowerPotBlock(WEPEAR_BERRY))
val POTTED_WEPEAR_BERRY = this.flowerPot("potted_wepear_berry", WEPEAR_BERRY)
@JvmField
val POTTED_WIKI_BERRY = this.create("potted_wiki_berry", BlocksInvoker.createFlowerPotBlock(WIKI_BERRY))
val POTTED_WIKI_BERRY = this.flowerPot("potted_wiki_berry", WIKI_BERRY)
@JvmField
val POTTED_YACHE_BERRY = this.create("potted_yache_berry", BlocksInvoker.createFlowerPotBlock(YACHE_BERRY))
val POTTED_YACHE_BERRY = this.flowerPot("potted_yache_berry", YACHE_BERRY)
@JvmField
val POTTED_PEP_UP_FLOWER = this.flowerPot("potted_pep_up_flower", PEP_UP_FLOWER)
@JvmField
val POTTED_RED_APRICORN_SAPLING = this.flowerPot("potted_red_apricorn_sapling", RED_APRICORN_SAPLING)
@JvmField
val POTTED_YELLOW_APRICORN_SAPLING = this.flowerPot("potted_yellow_apricorn_sapling", YELLOW_APRICORN_SAPLING)
@JvmField
val POTTED_GREEN_APRICORN_SAPLING = this.flowerPot("potted_green_apricorn_sapling", GREEN_APRICORN_SAPLING)
@JvmField
val POTTED_BLUE_APRICORN_SAPLING = this.flowerPot("potted_blue_apricorn_sapling", BLUE_APRICORN_SAPLING)
@JvmField
val POTTED_PINK_APRICORN_SAPLING = this.flowerPot("potted_pink_apricorn_sapling", PINK_APRICORN_SAPLING)
@JvmField
val POTTED_BLACK_APRICORN_SAPLING = this.flowerPot("potted_black_apricorn_sapling", BLACK_APRICORN_SAPLING)
@JvmField
val POTTED_WHITE_APRICORN_SAPLING = this.flowerPot("potted_white_apricorn_sapling", WHITE_APRICORN_SAPLING)
@JvmField
val POTTED_SACCHARINE_SAPLING = this.flowerPot("potted_saccharine_sapling", SACCHARINE_SAPLING)
init {
/**
@@ -1346,7 +1345,16 @@ object CobblemonBlocks : PlatformRegistry<Registry<Block>, ResourceKey<Registry<
fun typeGemClusters() = typeGemClusters.toMap()
private fun pottedPlant(name: String, flower: Block) = this.create(name, BlocksInvoker.createFlowerPotBlock(flower))
private fun flowerPot(name: String, flower: Block) = this.create(
name,
FlowerPotBlock(
flower,
BlockBehaviour.Properties.of()
.instabreak()
.noOcclusion()
.pushReaction(PushReaction.DESTROY)
)
)
private fun mintLitter(name: String, color: MapColor) = this.create(
name,
@@ -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)
}
}
}
@@ -9,6 +9,7 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
@@ -17,6 +18,7 @@ import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveBoolean
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.world.gamerules.CobblemonGameRules
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.BehaviorControl
@@ -34,6 +36,8 @@ import net.minecraft.world.entity.ai.sensing.SensorType
class HateEntityTaskConfig : SingleTaskConfig {
val entityCondition: Expression = "true".asExpression()
val range: ExpressionOrEntityVariable = Either.left("24".asExpression())
val chance: Float = Cobblemon.config.predationChance
private val coercedChance = chance.coerceIn(0f, 1f)
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = emptyList<MoLangConfigVariable>()
override fun createTask(
@@ -43,24 +47,44 @@ class HateEntityTaskConfig : SingleTaskConfig {
val range = range.resolveFloat(behaviourConfigurationContext.runtime)
behaviourConfigurationContext.addMemories(
MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES,
MemoryModuleType.ANGRY_AT
MemoryModuleType.ANGRY_AT,
MemoryModuleType.ATTACK_TARGET
)
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
return BehaviorBuilder.create { instance ->
instance.group(
instance.present(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES),
instance.registered(MemoryModuleType.ANGRY_AT)
).apply(instance) { entities, angryAt ->
instance.registered(MemoryModuleType.ANGRY_AT),
instance.registered(MemoryModuleType.ATTACK_TARGET),
).apply(instance) { entities, angryAt, attackTarget ->
Trigger { world, entity, _ ->
val target = instance.get(entities).findClosest {
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
// bail if gamerule is disabled
if (!world.gameRules.getBoolean(CobblemonGameRules.DO_POKEMON_PREDATION)) return@Trigger false
val target = instance.get(entities).findClosest { hated ->
mainThreadRuntime.withQueryValue("entity", hated.asMostSpecificMoLangValue())
return@findClosest mainThreadRuntime.resolveBoolean(entityCondition)
}.orElse(null)
var shouldTrigger = false
if (target != null && target.distanceTo(entity) <= range) {
angryAt.set(target.uuid)
shouldTrigger = when {
// set always attacking if activity chance is equal to 1.0
coercedChance == 1f -> {
angryAt.set(target.uuid)
attackTarget.set(target)
true
}
// roll the dice in other way
coercedChance != 1f && world.random.nextFloat() < coercedChance -> {
angryAt.set(target.uuid)
attackTarget.set(target)
true
}
// fallback to peaceful behavior in unattended cases
else -> false
}
}
return@Trigger true
return@Trigger shouldTrigger
}
}
}
@@ -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))
}
@@ -88,7 +88,7 @@ fun getToolTipHeaderIcon(tradeEvolution: TradeEvolution): CobblemonDexEvolutionR
lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
} else {
PokemonSpecies.getByIdentifier(species.asIdentifierDefaultingNamespace())
?.let { lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.green()) }
?.let { lang("ui.pokedex.evolution.trade.tooltip.with_species", it.translatedName.plainCopy().green()) }
?: lang("ui.pokedex.evolution.trade.tooltip.with_any_species")
}
return CobblemonDexEvolutionRequirementDisplay(CobblemonItems.LINK_CABLE, tooltip)
@@ -282,7 +282,7 @@ fun buildLevelRequirement(levelRequirement: LevelRequirement): DexEvolutionRequi
fun buildMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): DexEvolutionRequirementDisplay {
val icon = Items.CLOCK
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().green()
val moonPhaseComponent = moonPhaseRequirement.moonPhase.displayName().plainCopy().green()
val tooltip = lang("ui.pokedex.requirement.moon_phase.tooltip", moonPhaseComponent)
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
@@ -290,7 +290,7 @@ fun buildMoonPhaseRequirement(moonPhaseRequirement: MoonPhaseRequirement): DexEv
fun buildMoveSetRequirement(moveSetRequirement: MoveSetRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.TECHNICAL_MACHINE
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.green())
val tooltip = lang("ui.pokedex.requirement.has_move.tooltip", moveSetRequirement.move.displayName.plainCopy().green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
@@ -319,7 +319,7 @@ fun buildMoveTypeRequirement(moveTypeRequirement: MoveTypeRequirement): DexEvolu
else -> CobblemonItems.NORMAL_GEM
}
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.green())
val tooltip = lang("ui.pokedex.requirement.has_move_type.tooltip", moveTypeRequirement.type.displayName.plainCopy().green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
@@ -437,7 +437,7 @@ fun buildStructureRequirement(structureRequirement: StructureRequirement, negate
fun buildTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): DexEvolutionRequirementDisplay? {
val icon = Items.CLOCK
val timeRangeComponent = timeRangeRequirement.range.displayName() ?: return null
val timeRangeComponent = timeRangeRequirement.range.displayName()?.plainCopy() ?: return null
val tooltip = lang("ui.pokedex.requirement.time_range.tooltip", timeRangeComponent.green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
@@ -445,7 +445,7 @@ fun buildTimeRangeRequirement(timeRangeRequirement: TimeRangeRequirement): DexEv
fun buildUseMoveRequirement(useMoveRequirement: UseMoveRequirement): DexEvolutionRequirementDisplay {
val icon = CobblemonItems.CHOICE_BAND
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.green())
val tooltip = lang("ui.pokedex.requirement.use_move.tooltip", useMoveRequirement.move.displayName.plainCopy().green())
return CobblemonDexEvolutionRequirementDisplay(icon, tooltip)
}
@@ -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)
@@ -42,8 +42,8 @@ abstract class AreaTypeSpawningCondition<T : AreaSpawnablePosition> : SpawningCo
override fun copyFrom(other: SpawningCondition<*>, merger: Merger) {
super.copyFrom(other, merger)
if (other is AreaTypeSpawningCondition) {
merger.mergeSingle(minHeight, other.minHeight)
merger.mergeSingle(maxHeight, other.maxHeight)
minHeight = merger.mergeSingle(minHeight, other.minHeight)
maxHeight = merger.mergeSingle(maxHeight, other.maxHeight)
neededNearbyBlocks = merger.merge(neededNearbyBlocks, other.neededNearbyBlocks)?.toMutableList()
}
}
@@ -46,7 +46,7 @@ abstract class SubmergedTypeSpawningCondition<T : SubmergedSpawnablePosition> :
super.copyFrom(other, merger)
if (other is SubmergedTypeSpawningCondition) {
minDepth = merger.mergeSingle(minDepth, other.minDepth)
maxDepth = merger.mergeSingle(minDepth, other.minDepth)
maxDepth = merger.mergeSingle(maxDepth, other.maxDepth)
fluidIsSource = merger.mergeSingle(fluidIsSource, other.fluidIsSource)
fluid = merger.mergeSingle(fluid, other.fluid)
}
@@ -39,7 +39,7 @@ abstract class SurfaceTypeSpawningCondition<T : SurfaceSpawnablePosition> : Area
super.copyFrom(other, merger)
if (other is SurfaceTypeSpawningCondition) {
minDepth = merger.mergeSingle(minDepth, other.minDepth)
maxDepth = merger.mergeSingle(minDepth, other.minDepth)
maxDepth = merger.mergeSingle(maxDepth, other.maxDepth)
fluid = merger.mergeSingle(fluid, other.fluid)
}
}
@@ -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()
}
}
@@ -85,6 +85,7 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
if (HidePartyBinding.shouldHide) {
return false
}
return true
}
}
@@ -558,4 +559,4 @@ class PartyOverlay : Gui(Minecraft.getInstance()) {
}
}
}
}
}
@@ -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 {
}
*/
}
}
}
@@ -34,6 +34,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.bedrock.animatio
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.ModelQuirk
import com.cobblemon.mod.common.client.render.models.blockbench.quirk.QuirkData
import com.cobblemon.mod.common.client.sound.instances.UIEntitySoundInstance
import com.cobblemon.mod.common.entity.PosableEntity
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
@@ -44,7 +45,6 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException
import java.util.concurrent.ConcurrentLinkedQueue
import net.minecraft.client.Minecraft
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.commands.arguments.item.ItemParser
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.Entity
@@ -289,7 +289,7 @@ abstract class PosableState : Schedulable {
entity.level().playLocalSound(entity, soundEvent, entity.soundSource, volume, pitch)
}
} else {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, volume, pitch))
Minecraft.getInstance().soundManager.play(UIEntitySoundInstance(soundEvent, volume, pitch))
}
}
}
@@ -19,6 +19,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Bone
import com.cobblemon.mod.common.client.render.models.blockbench.repository.RenderContext
import com.cobblemon.mod.common.client.sound.instances.AlphaCrySoundInstance
import com.cobblemon.mod.common.client.sound.instances.UIEntitySoundInstance
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.util.effectiveName
import com.cobblemon.mod.common.util.genericRuntime
@@ -31,7 +32,6 @@ import net.minecraft.client.Minecraft
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.client.multiplayer.ClientLevel
import net.minecraft.client.resources.sounds.EntityBoundSoundInstance
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.resources.ResourceLocation
import net.minecraft.sounds.SoundEvent
import net.minecraft.world.entity.Entity
@@ -121,7 +121,7 @@ class BedrockSoundKeyframe(
}
}
} else {
Minecraft.getInstance().soundManager.play(SimpleSoundInstance.forUI(soundEvent, 1F, 1F))
Minecraft.getInstance().soundManager.play(UIEntitySoundInstance(soundEvent))
}
}
}
@@ -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") }
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asExpressionLike
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
@@ -28,6 +29,8 @@ class MasquerainModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame
lateinit var walk: Pose
lateinit var standing: Pose
override fun registerPoses() {
animations["cry"] = "q.bedrock_stateful('masquerain', 'cry')".asExpressionLike()
val blink = quirk { bedrockStateful("masquerain", "blink") }
standing = registerPose(
poseName = "standing",
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.util.isInWater
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
@@ -36,6 +37,8 @@ class SurskitModel(root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
val wateroffset = -8
override fun registerPoses() {
animations["cry"] = "q.bedrock_stateful('surskit', 'cry')".asExpressionLike()
val blink = quirk { bedrockStateful("surskit", "blink") }
standing = registerPose(
poseName = "standing",
@@ -14,6 +14,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
import com.cobblemon.mod.common.client.render.models.blockbench.pose.ModelPartTransformation
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asExpressionLike
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
@@ -36,6 +37,8 @@ class CombeeModel (root: ModelPart) : PokemonPosableModel(root) {
val shoulderZOffset = 8
override fun registerPoses() {
animations["cry"] = "q.bedrock_stateful('combee', 'cry')".asExpressionLike()
val blink1 = quirk { bedrockStateful("combee", "blink_right") }
val blink2 = quirk { bedrockStateful("combee", "blink_gender") }
val blink3 = quirk { bedrockStateful("combee", "blink_left") }
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonP
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import com.cobblemon.mod.common.util.asExpressionLike
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
@@ -30,6 +31,8 @@ class VespiquenModel (root: ModelPart) : PokemonPosableModel(root) {
lateinit var standing: Pose
override fun registerPoses() {
animations["cry"] = "q.bedrock_stateful('vespiquen', 'cry')".asExpressionLike()
val blink1 = quirk { bedrockStateful("vespiquen", "blink") }
val wingsleep = quirk { bedrockStateful("vespiquen", "sleep_flap") }
@@ -1,62 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SerperiorModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("serperior")
override val head = getPart("head_ai")
override var portraitScale = 2.5F
override var portraitTranslation = Vec3(-0.3, 1.25, 0.0)
override var profileScale = 0.6F
override var profileTranslation = Vec3(0.0, 0.8, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var sleep: Pose
override val cryAnimation = CryProvider { bedrockStateful("serperior", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("serperior", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
animations = arrayOf(bedrock("serperior", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("serperior", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("serperior", "ground_walk")
)
)
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
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.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class ServineModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("servine")
override val head = getPart("head_ai")
override val rightArm = getPart("arm_right")
override val leftArm = getPart("arm_left")
override val rightLeg = getPart("leg_right")
override val leftLeg = getPart("leg_left")
override var portraitScale = 2.6F
override var portraitTranslation = Vec3(-0.15, 0.75, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
override val cryAnimation = CryProvider { bedrockStateful("servine", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("servine", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
animations = arrayOf(bedrock("servine", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("servine", "ground_idle")
)
)
walk = registerPose(
poseName = "walking",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("servine", "ground_walk")
)
)
}
}
@@ -1,70 +0,0 @@
/*
* Copyright (C) 2023 Cobblemon Contributors
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package com.cobblemon.mod.common.client.render.models.blockbench.pokemon.gen5
import com.cobblemon.mod.common.client.render.models.blockbench.frame.BimanualFrame
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.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class SnivyModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame, BipedFrame, BimanualFrame {
override val rootPart = root.registerChildWithAllChildren("snivy")
override val head = getPart("head_ai")
override val rightArm = getPart("arm_right")
override val leftArm = getPart("arm_left")
override val rightLeg = getPart("leg_right")
override val leftLeg = getPart("leg_left")
override var portraitScale = 2.0F
override var portraitTranslation = Vec3(-0.15, 0.32, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
lateinit var sleep: Pose
lateinit var standing: Pose
lateinit var walk: Pose
override val cryAnimation = CryProvider { bedrockStateful("snivy", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("snivy", "blink") }
sleep = registerPose(
poseType = PoseType.SLEEP,
transformTicks = 10,
animations = arrayOf(bedrock("snivy", "sleep"))
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("snivy", "ground_idle")
)
)
walk = registerPose(
poseName = "walking",
poseTypes = PoseType.MOVING_POSES,
transformTicks = 10,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("snivy", "ground_walk")
)
)
}
}
@@ -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
@@ -1,85 +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.gen6
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.Pose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class GourgeistModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("gourgeist")
override val head = getPart("head")
override var portraitScale = 4.0F
override var portraitTranslation = Vec3(-0.1, 0.6, 0.0)
override var profileScale = 0.8F
override var profileTranslation = Vec3(0.0, 0.55, 0.0)
lateinit var standing: Pose
lateinit var walk: Pose
lateinit var hovering: Pose
lateinit var fly: Pose
lateinit var sleep: Pose
override val cryAnimation = CryProvider { bedrockStateful("gourgeist", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("gourgeist", "blink") }
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.UI_POSES + PoseType.STAND,
quirks = arrayOf(blink),
animations = arrayOf(
singleBoneLook(),
bedrock("gourgeist", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseType = PoseType.WALK,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("gourgeist", "ground_walk")
)
)
hovering = registerPose(
poseName = "hovering",
poseType = PoseType.HOVER,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("gourgeist", "air_idle")
)
)
fly = registerPose(
poseName = "fly",
poseType = PoseType.FLY,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("gourgeist", "air_fly")
)
)
sleep = registerPose(
poseName = "sleep",
poseType = PoseType.SLEEP,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("gourgeist", "sleep")
)
)
}
}
@@ -1,77 +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.gen6
import com.cobblemon.mod.common.client.render.models.blockbench.frame.HeadedFrame
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.CryProvider
import com.cobblemon.mod.common.client.render.models.blockbench.pokemon.PokemonPosableModel
import com.cobblemon.mod.common.client.render.models.blockbench.pose.CobblemonPose
import com.cobblemon.mod.common.entity.PoseType
import net.minecraft.client.model.geom.ModelPart
import net.minecraft.world.phys.Vec3
class PumpkabooModel (root: ModelPart) : PokemonPosableModel(root), HeadedFrame {
override val rootPart = root.registerChildWithAllChildren("pumpkaboo")
override val head = getPart("head")
override var portraitScale = 3.5F
override var portraitTranslation = Vec3(0.0, -1.5, 0.0)
override var profileScale = 1.0F
override var profileTranslation = Vec3(0.0, 0.25, 0.0)
lateinit var standing: CobblemonPose
lateinit var walk: CobblemonPose
lateinit var battleidle: CobblemonPose
lateinit var sleep: CobblemonPose
override val cryAnimation = CryProvider { bedrockStateful("pumpkaboo", "cry") }
override fun registerPoses() {
val blink = quirk { bedrockStateful("pumpkaboo", "blink") }
val quirk = quirk { bedrockStateful("pumpkaboo", "quirk_idle") }
sleep = registerPose(
poseName = "sleep",
poseType = PoseType.SLEEP,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("pumpkaboo", "sleep")
)
)
standing = registerPose(
poseName = "standing",
poseTypes = PoseType.STATIONARY_POSES + PoseType.UI_POSES,
quirks = arrayOf(blink, quirk),
animations = arrayOf(
singleBoneLook(),
bedrock("pumpkaboo", "ground_idle")
)
)
walk = registerPose(
poseName = "walk",
poseTypes = PoseType.MOVING_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("pumpkaboo", "ground_walk")
)
)
battleidle = registerPose(
poseName = "battleidle",
poseTypes = PoseType.STATIONARY_POSES,
quirks = arrayOf(blink),
animations = arrayOf(
bedrock("pumpkaboo", "battle_idle")
)
)
}
}
@@ -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)
@@ -322,8 +272,6 @@ object VaryingModelRepository {
inbuilt("loudred", :: LoudredModel)
inbuilt("exploud", :: ExploudModel)
inbuilt("cryogonal", :: CryogonalModel)
inbuilt("pumpkaboo", :: PumpkabooModel)
inbuilt("gourgeist", :: GourgeistModel)
inbuilt("eiscue", :: EiscueModel)
inbuilt("wooloo", :: WoolooModel)
inbuilt("dubwool", :: DubwoolModel)
@@ -340,9 +288,6 @@ object VaryingModelRepository {
inbuilt("thwackey", ::ThwackeyModel)
inbuilt("rillaboom", ::RillaboomModel)
inbuilt("samurott", ::SamurottModel)
inbuilt("snivy", ::SnivyModel)
inbuilt("servine", ::ServineModel)
inbuilt("serperior", ::SerperiorModel)
inbuilt("slugma", ::SlugmaModel)
inbuilt("magcargo", ::MagcargoModel)
inbuilt("slugma_shiny", ::SlugmaShinyModel)
@@ -402,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)
@@ -672,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
@@ -0,0 +1,22 @@
/*
* 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.sound.instances
import net.minecraft.client.resources.sounds.SimpleSoundInstance
import net.minecraft.client.resources.sounds.SoundInstance
import net.minecraft.sounds.SoundEvent
import net.minecraft.sounds.SoundSource
/**
* Plays a sound for a Pokémon rendered in a GUI, where there is no entity to take the sound source from.
* Unlike [SimpleSoundInstance.forUI] this uses [SoundSource.NEUTRAL] so the Friendly Creatures slider applies.
*/
class UIEntitySoundInstance(sound: SoundEvent, volume: Float = 1F, pitch: Float = 1F) :
SimpleSoundInstance(sound.location, SoundSource.NEUTRAL, volume, pitch, SoundInstance.createUnseededRandom(),
false, 0, SoundInstance.Attenuation.NONE, 0.0, 0.0, 0.0, true)
@@ -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
}
}
}
@@ -164,6 +164,9 @@ class CobblemonConfig {
@CobblemonConfigField(Category.Pokemon, lang = "experience_multiplier", SERVER)
var experienceMultiplier = 2F
@CobblemonConfigField(Category.Pokemon, lang = "predation_chance", SERVER)
var predationChance = 0.05F
@CobblemonConfigField(Category.Spawning, lang = "pokemon_per_chunk", SERVER)
var pokemonPerChunk = 1F
@@ -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
}
}
@@ -1294,16 +1294,7 @@ open class PokemonEntity(
}
private fun showInteractionWheel(player: ServerPlayer, itemStack: ItemStack) {
val canRide = ifRidingAvailableSupply(false) { behaviour, settings, state ->
if (platform != PlatformType.NONE) return@ifRidingAvailableSupply false
if (tethering != null) return@ifRidingAvailableSupply false;
if (seats.isEmpty()) return@ifRidingAvailableSupply false;
if ((owner as? ServerPlayer)?.isInBattle() == true) return@ifRidingAvailableSupply false;
if (this.owner != player && this.passengers.isEmpty()) return@ifRidingAvailableSupply false;
if(pokemon.effectiveScale < Cobblemon.config.minimumRidingScale) return@ifRidingAvailableSupply false;
if(beamMode == 3) return@ifRidingAvailableSupply false;
return@ifRidingAvailableSupply behaviour.isActive(settings, state, this);
}
val canRide = isRideableFor(player)
if (pokemon.getOwnerPlayer() == player) {
val cosmeticItemDefinition = CobblemonCosmeticItems.findValidCosmeticForPokemonAndItem(
player.level().registryAccess(),
@@ -1325,6 +1316,18 @@ open class PokemonEntity(
}
}
fun isRideableFor(player: ServerPlayer): Boolean = ifRidingAvailableSupply(false) canRide@{ behaviour, settings, state ->
if (!player.canInteractWithEntity(this, player.entityInteractionRange())) return@canRide false
if (platform != PlatformType.NONE) return@canRide false
if (tethering != null) return@canRide false
if (seats.isEmpty()) return@canRide false
if ((owner as? ServerPlayer)?.isInBattle() == true) return@canRide false
if (this.owner != player && this.passengers.isEmpty()) return@canRide false
if (pokemon.effectiveScale < Cobblemon.config.minimumRidingScale) return@canRide false
if (beamMode == 3) return@canRide false
return@canRide behaviour.isActive(settings, state, this)
}
override fun getDimensions(pose: Pose): EntityDimensions {
val scale = effects.mockEffect?.scale ?: (form.baseScale * pokemon.effectiveScale)
var result = this.exposedForm.hitbox.scale(scale)
@@ -1606,9 +1609,8 @@ open class PokemonEntity(
|| stack.`is`(CobblemonItemTags.WHITELISTED_ITEMS_TO_HOLD)
fun tryRidingPokemon(player: ServerPlayer): Boolean {
if (pokemon.effectiveScale < Cobblemon.config.minimumRidingScale) {
return false
}
if (!isRideableFor(player)) return false
val event = RidePokemonEvent.Pre(player, this)
CobblemonEvents.RIDE_EVENT_PRE.post(event)
if (!event.isCanceled) {
@@ -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) {
@@ -55,6 +55,8 @@ object PlatformEvents {
@JvmField
val CLIENT_PLAYER_LOGOUT = EventObservable<ClientPlayerEvent.Logout>()
@JvmField
val PLAYER_RESPAWN = EventObservable<ServerPlayerEvent.Respawn>()
@JvmField
val PLAYER_DEATH = CancelableObservable<ServerPlayerEvent.Death>()
@JvmField
val RIGHT_CLICK_BLOCK = CancelableObservable<ServerPlayerEvent.RightClickBlock>()
@@ -50,6 +50,11 @@ interface ServerPlayerEvent {
*/
data class Logout(override val player: ServerPlayer) : ServerPlayerEvent
/**
* Fired when the [player] respawns (either by coming back from the end or dying).
*/
data class Respawn(override val player: ServerPlayer) : ServerPlayerEvent
/**
* Fired when the [player] dies.
* If canceled the death will be prevented but healing is required in order to not be stuck in a loop.
@@ -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))
@@ -135,17 +135,18 @@ internal object PropertiesCompletionProvider : DataRegistry {
private fun addDefaults() {
this.inject(setOf("level", "lvl", "l"), setOf("1", "${Cobblemon.config.maxPokemonLevel}") )
this.inject(setOf("shiny", "s"), setOf("yes", "no"))
this.inject(setOf("alpha", "is_alpha"), setOf("yes", "no"))
this.inject(setOf("gender"), Gender.entries.map { it.name.lowercase() })
this.inject(setOf("friendship"), setOf("0", Cobblemon.config.maxPokemonFriendship.toString()))
this.inject(setOf("fullness"), setOf("0", "255"))
this.inject(setOf("pokeball"), PokeBalls.all().map { it.name.simplify() })
this.inject(setOf("nature"), Natures.all().map { it.name.simplify() })
this.inject(setOf("ability"), Abilities.all().map { if (it.name.asIdentifierDefaultingNamespace().namespace == Cobblemon.MODID) it.name.asIdentifierDefaultingNamespace().path else it.name })
this.inject(setOf("dmax"), setOf("0", Cobblemon.config.maxDynamaxLevel.toString()))
this.inject(setOf("gmax"), setOf("yes", "no"))
this.inject(setOf("dmax_level", "dmax"), setOf("0", Cobblemon.config.maxDynamaxLevel.toString()))
this.inject(setOf("gmax_factor", "gmax"), setOf("yes", "no"))
this.inject(setOf("type", "elemental_type"), ElementalTypes.all().map { it.name })
this.inject(setOf("tera_type", "tera"), TeraTypes.map { it.id.simplify() })
this.inject(setOf("tradeable"), setOf("yes", "no"))
this.inject(setOf("tradeable", "tradable"), setOf("yes", "no"))
this.inject(setOf("originaltrainer", "ot"), setOf(""))
this.inject(setOf("originaltrainertype", "ottype"), setOf("None", "Player", "NPC"))
this.inject(setOf("nickname", "nick"), setOf(""))
@@ -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()
}
}
@@ -26,5 +26,6 @@ object CobblemonGameRules {
val SHINY_STARTERS: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("doShinyStarters", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
@JvmField
val HEALERS_HEAL_PC: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("healersHealPC", GameRules.Category.MISC, BooleanRuleInvoker.`cobblemon$create`(false))
@JvmField
val DO_POKEMON_PREDATION: GameRules.Key<GameRules.BooleanValue> = Cobblemon.implementation.registerGameRule("doPokemonPredation", GameRules.Category.MOBS, BooleanRuleInvoker.`cobblemon$create`(true))
}
@@ -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]
}
}
}
}
Loaded 100 of 855 files, more files were not shown because too many files have changed in this diff. Show more