Merge branch 'main' into riding-controllers-expanded
# Conflicts: # common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/land/VehicleBehaviour.kt # common/src/main/kotlin/com/cobblemon/mod/common/api/riding/behaviour/types/liquid/BoatBehaviour.kt # common/src/main/kotlin/com/cobblemon/mod/common/entity/pokemon/PokemonEntity.kt # common/src/main/resources/assets/cobblemon/sounds.json # common/src/main/resources/data/cobblemon/species/generation1/charizard.json
@@ -12,9 +12,11 @@
|
||||
|
||||
### Developer
|
||||
- baseScale in NPCs has been removed from classes and presets, and in the entity it is replaced with hitboxScale and renderScale.
|
||||
- Added `EntityCallbacks` to `NPCEntity` and `PokemonEntity`. You can trigger custom callback types by using `[Entity].callbacks.process(...)`
|
||||
|
||||
### MoLang & Datapacks
|
||||
- Added a `behaviours` datapack folder which motivates the Behaviour Editor screen on NPCs and Pokémon.
|
||||
- Added "callback" support on PokemonEntity and NPCEntity that allows MoLang scripts to be executed in response to certain scenarios.
|
||||
- Added very many behaviours to choose from.
|
||||
- Removed many NPC presets that now exist as behaviours. This is a breaking change if you are using NPCs with these presets.
|
||||
- NPCs with these presets should be modified to instead list under `"behaviours"` all the equivalent behaviours.
|
||||
@@ -307,6 +307,8 @@
|
||||
- Fixed Moon Ball moon phase logic to actually work correctly
|
||||
- Fixed `/pokedex printcalculations` to now show the correct percentage completed of the Pokedex
|
||||
- Fixed mod incompatibility with the `Raised` mod
|
||||
- Fixed a vulnerability that could cause party and PC rollbacks under specific circumstances.
|
||||
- Fixed a rare edge case where sorting your PC could be rolled back later.
|
||||
|
||||
### Developer
|
||||
- A finished battle now has winners and losers set inside of `PokemonBattle` instead of them always being empty.
|
||||
@@ -347,7 +349,8 @@
|
||||
- Added PokedexManager.obtain as a replacement for .catch which is not a friendly function name in Java.
|
||||
|
||||
- Added `Pokemon#hyperTrainIV()` and `IVs#setHyperTrainedIV(Stat, Int)`
|
||||
- `ElementalType` now implelments `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
|
||||
- `ElementalType` now implements `ShowdownIdentifiable` to ensure the communcation with showdown stays consistent (also in regards to TeraTypes)
|
||||
- Pokemon no longer have a change observable
|
||||
|
||||
### MoLang & Datapacks
|
||||
- The following usages for item predicates can now use item conditions like advancements do, you can learn about them in the [Minecraft wiki](https://minecraft.wiki/w/Advancement_definition#minecraft:filled_bucket)
|
||||
@@ -391,6 +394,10 @@
|
||||
- Adds Flows for `STARTER_CHOSEN`, `EV_GAINED`, `POKEMON_RELEASED`, `POKEMON_NICKNAMED`, `HELD_ITEM`, and `TRADE_COMPLETED` events
|
||||
- Adds Pokemon functions for `pokeball`, `held_item`, `remove_held_item`, `add_aspects`, and `remove_aspects`
|
||||
- Added `pokemon.hyper_train_iv` as an available Molang function.
|
||||
- Added `prepare_{effect}` and `damage_{effect}` action effect hooks in battles for more battle particle effects.
|
||||
- Added `q.has_argument(<argument_name>, [argument_value])` MoLang function to several battle-related action effect contexts.
|
||||
- Added `q.has_argument_at(<index>, [argument_value])` MoLang function to several battle-related action effect contexts.
|
||||
- Added `q.hit_count` MoLang function to move action effect contexts.
|
||||
|
||||
## [1.6.1 (January 26th, 2025)](#1-6-1)
|
||||
|
||||
|
||||
@@ -75,6 +75,7 @@ import com.cobblemon.mod.common.config.constraint.IntConstraint
|
||||
import com.cobblemon.mod.common.config.starter.StarterConfig
|
||||
import com.cobblemon.mod.common.data.CobblemonDataProvider
|
||||
import com.cobblemon.mod.common.events.AdvancementHandler
|
||||
import com.cobblemon.mod.common.events.EntityCallbackHandler
|
||||
import com.cobblemon.mod.common.events.FlowHandler
|
||||
import com.cobblemon.mod.common.events.PokedexHandler
|
||||
import com.cobblemon.mod.common.events.ServerTickHandler
|
||||
@@ -313,6 +314,7 @@ object Cobblemon {
|
||||
CobblemonEvents.POKEMON_PROPERTY_INITIALISED.emit(Unit)
|
||||
|
||||
FlowHandler.setup()
|
||||
EntityCallbackHandler.setup()
|
||||
|
||||
ifDedicatedServer {
|
||||
isDedicatedServer = true
|
||||
|
||||
@@ -8,8 +8,15 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.ai
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.ai.config.BehaviourConfig
|
||||
import com.cobblemon.mod.common.api.molang.ExpressionLike
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.mojang.serialization.Dynamic
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -28,6 +35,9 @@ class BehaviourConfigurationContext {
|
||||
val memories = mutableSetOf<MemoryModuleType<*>>()
|
||||
val sensors = mutableSetOf<SensorType<*>>()
|
||||
|
||||
val onAddScripts = mutableListOf<ResourceLocation>()
|
||||
val onAdd = mutableListOf<ExpressionLike>()
|
||||
|
||||
fun getOrCreateActivity(activity: Activity): ActivityConfigurationContext {
|
||||
return activities.firstOrNull { it.activity == activity } ?: ActivityConfigurationContext(activity).also(activities::add)
|
||||
}
|
||||
@@ -48,6 +58,14 @@ class BehaviourConfigurationContext {
|
||||
this.sensors.addAll(sensors)
|
||||
}
|
||||
|
||||
fun addOnAddScript(script: ResourceLocation) {
|
||||
onAddScripts.add(script)
|
||||
}
|
||||
|
||||
fun addOnAddScript(script: ExpressionLike) {
|
||||
onAdd.add(script)
|
||||
}
|
||||
|
||||
fun apply(entity: LivingEntity, behaviourConfigs: List<BehaviourConfig>, dynamic: Dynamic<*>) {
|
||||
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
@@ -62,6 +80,11 @@ class BehaviourConfigurationContext {
|
||||
// Apply the brain config
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
brain = entity.assignNewBrainWithMemoriesAndSensors(dynamic, memories, sensors)
|
||||
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
onAddScripts.forEach { CobblemonScripts.run(it, runtime) }
|
||||
onAdd.forEach { runtime.resolve(it) }
|
||||
}
|
||||
activities.forEach { it.apply(entity) }
|
||||
brain.setCoreActivities(coreActivities)
|
||||
|
||||
@@ -9,7 +9,17 @@
|
||||
package com.cobblemon.mod.common.api.ai
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* This is used in the AI system to store optional additional information about the attack target,
|
||||
@@ -30,5 +40,32 @@ import com.cobblemon.mod.common.util.asExpression
|
||||
*/
|
||||
class CobblemonAttackTargetData(
|
||||
/** An expression that returns false when the target is no longer worth attacking. */
|
||||
val shouldContinue: Expression = "true".asExpression()
|
||||
)
|
||||
var shouldContinue: Expression = "true".asExpression(),
|
||||
var hitsUntilDisengage: Int = -1, // -1 means no limit
|
||||
var disengageAction: (entity: Entity) -> Unit = {}
|
||||
) {
|
||||
val struct = ObjectValue<CobblemonAttackTargetData>(this).also {
|
||||
it.addFunction("hits_until_disengage") { DoubleValue(hitsUntilDisengage.toDouble()) }
|
||||
it.addFunction("set_hits_until_disengage") { params ->
|
||||
val value = params.getDouble(0)
|
||||
hitsUntilDisengage = value.toInt()
|
||||
DoubleValue(hitsUntilDisengage.toDouble())
|
||||
}
|
||||
it.addFunction("set_disengage_action") { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val function = CobblemonScripts.scripts[script] ?: run {
|
||||
Cobblemon.LOGGER.warn("set_disengage_action was given script '$script' which does not exist.")
|
||||
return@addFunction DoubleValue.ZERO
|
||||
}
|
||||
disengageAction = { entity ->
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.environment.query.addFunction("entity") { entity.asMostSpecificMoLangValue() }
|
||||
runtime.resolve(function)
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
it.addFunction("unset_disengage_action") { params ->
|
||||
disengageAction = {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.util.asTranslated
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import com.cobblemon.mod.common.util.withQueryValue
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import net.minecraft.network.chat.Component
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
@@ -28,23 +29,35 @@ class CobblemonBehaviour(
|
||||
val visible: Boolean = true,
|
||||
val entityType: ResourceLocation? = null,
|
||||
val configurations: List<BehaviourConfig> = mutableListOf(),
|
||||
val undo: ExpressionLike? = null,
|
||||
val undoScript: ResourceLocation? = null,
|
||||
// I feel like the onAdd+onAddScript etc could be merged using some interface and a clever deserializer,
|
||||
// detect if it's a ResourceLocation and failing that, Expression. The on[..]Script fields are kinda fringe though.
|
||||
@SerializedName("onRemove", alternate = ["undo"])
|
||||
val onRemove: ExpressionLike? = null,
|
||||
@SerializedName("onRemoveScript", alternate = ["undoScript"])
|
||||
val onRemoveScript: ResourceLocation? = null,
|
||||
val onAdd: ExpressionLike? = null,
|
||||
val onAddScript: ResourceLocation? = null,
|
||||
) {
|
||||
fun canBeApplied(entity: LivingEntity) = entityType?.let { entityType == entity.type.builtInRegistryHolder().unwrapKey().get().location() } != false
|
||||
fun configure(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) {
|
||||
if (onAdd != null) {
|
||||
behaviourConfigurationContext.addOnAddScript(onAdd)
|
||||
}
|
||||
if (onAddScript != null) {
|
||||
behaviourConfigurationContext.addOnAddScript(onAddScript)
|
||||
}
|
||||
configurations.forEach { it.configure(entity, behaviourConfigurationContext) }
|
||||
}
|
||||
|
||||
/** Undoes anything that needs undoing once this configuration is being removed from an entity that had it before. */
|
||||
fun undo(entity: LivingEntity) {
|
||||
fun onRemove(entity: LivingEntity) {
|
||||
val runtime = MoLangRuntime().setup()
|
||||
runtime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
if (undoScript != null) {
|
||||
CobblemonScripts.run(undoScript, runtime)
|
||||
if (onRemoveScript != null) {
|
||||
CobblemonScripts.run(onRemoveScript, runtime)
|
||||
}
|
||||
if (undo != null) {
|
||||
runtime.resolve(undo)
|
||||
if (onRemove != null) {
|
||||
runtime.resolve(onRemove)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.ai
|
||||
|
||||
import net.minecraft.world.entity.ai.behavior.BlockPosTracker
|
||||
import net.minecraft.world.phys.Vec3
|
||||
|
||||
/**
|
||||
* Wraps around [BlockPosTracker] to add additional flags for Cobblemon-specific behavior.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 4th, 2025
|
||||
*/
|
||||
class CobblemonBlockPosTracker(
|
||||
pos: Vec3,
|
||||
val flags: Set<String> = emptySet(),
|
||||
) : BlockPosTracker(pos) {
|
||||
companion object {
|
||||
const val BATTLE_LOOK_BYPASS_FLAG = "battle_look_bypass"
|
||||
}
|
||||
|
||||
fun isBattleLookBypass() = flags.contains(BATTLE_LOOK_BYPASS_FLAG)
|
||||
}
|
||||
@@ -108,7 +108,7 @@ class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
completionRange = 0
|
||||
)
|
||||
)
|
||||
lookTarget.set(BlockPosTracker(target /* trust me dude. */))
|
||||
lookTarget.set(BlockPosTracker(target))
|
||||
return@Trigger true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.Cobblemon
|
||||
import com.cobblemon.mod.common.Cobblemon.LOGGER
|
||||
import com.cobblemon.mod.common.CobblemonBuildDetails
|
||||
import com.cobblemon.mod.common.CobblemonNetwork
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
@@ -77,7 +78,7 @@ open class PokemonBattle(
|
||||
val side2: BattleSide
|
||||
) {
|
||||
/** Whether logging will be silenced for this battle. */
|
||||
var mute = true
|
||||
var mute = !CobblemonBuildDetails.SNAPSHOT
|
||||
val struct = this.asMoLangValue()
|
||||
val runtime = MoLangRuntime().also { it.environment.query = struct }
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ package com.cobblemon.mod.common.api.events.pokeball
|
||||
|
||||
import com.cobblemon.mod.common.api.events.Cancelable
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.moLangFunctionMap
|
||||
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
|
||||
@@ -20,8 +21,9 @@ class ThrownPokeballHitEvent(
|
||||
val pokeBall : EmptyPokeBallEntity,
|
||||
val pokemon : PokemonEntity
|
||||
) : Cancelable() {
|
||||
val context = mutableMapOf(
|
||||
"pokeball" to pokeBall.struct,
|
||||
"pokemon" to pokemon.asMoLangValue()
|
||||
val functions = moLangFunctionMap(
|
||||
"pokeball" to { pokeBall.struct },
|
||||
"pokemon" to { pokemon.asMoLangValue() },
|
||||
cancelFunc
|
||||
)
|
||||
}
|
||||
@@ -23,7 +23,9 @@ import com.cobblemon.mod.common.CobblemonBlockEntities
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonUnlockableWallpapers
|
||||
import com.cobblemon.mod.common.Environment
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonBlockPosTracker
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.ActorType
|
||||
import com.cobblemon.mod.common.api.battles.model.actor.BattleActor
|
||||
@@ -32,7 +34,6 @@ import com.cobblemon.mod.common.api.dialogue.ReferenceDialogueFaceProvider
|
||||
import com.cobblemon.mod.common.api.drop.DropEntry
|
||||
import com.cobblemon.mod.common.api.mark.Marks
|
||||
import com.cobblemon.mod.common.api.moves.BenchedMove
|
||||
import com.cobblemon.mod.common.api.moves.MoveTemplate
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
@@ -57,6 +58,7 @@ import com.cobblemon.mod.common.api.scheduling.ClientTaskTracker
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
import com.cobblemon.mod.common.api.scheduling.ServerTaskTracker
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.api.spawning.TimeRange
|
||||
import com.cobblemon.mod.common.api.spawning.position.SpawnablePosition
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
@@ -68,13 +70,11 @@ import com.cobblemon.mod.common.api.tags.CobblemonItemTags
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleFormat
|
||||
import com.cobblemon.mod.common.battles.BattleFormat.Companion.GEN_9_DOUBLES
|
||||
import com.cobblemon.mod.common.battles.BattleFormat.Companion.GEN_9_SINGLES
|
||||
import com.cobblemon.mod.common.battles.BattleFormat.Companion.GEN_9_TRIPLES
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.actor.PlayerBattleActor
|
||||
import com.cobblemon.mod.common.battles.actor.PokemonBattleActor
|
||||
import com.cobblemon.mod.common.client.render.models.blockbench.wavefunction.WaveFunctions
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
import com.cobblemon.mod.common.entity.npc.NPCBattleActor
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
@@ -305,6 +305,12 @@ object MoLangFunctions {
|
||||
{ worldHolder ->
|
||||
val world = worldHolder.value()
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_time_of_day") { params ->
|
||||
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
|
||||
?: return@put DoubleValue.ZERO
|
||||
val time = world.dayTime % 24000
|
||||
return@put DoubleValue(timeOfDay.contains(time.toInt()))
|
||||
}
|
||||
map.put("game_time") { _ -> DoubleValue(world.gameTime.toDouble()) }
|
||||
map.put("time_of_day") {
|
||||
val time = world.dayTime % 24000
|
||||
@@ -688,6 +694,34 @@ object MoLangFunctions {
|
||||
return@put owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO
|
||||
}
|
||||
|
||||
if (entity is MoLangScriptingEntity) {
|
||||
map.put("add_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
val allowDuplicates = params.getBooleanOrNull(2) ?: false
|
||||
entity.callbacks.addCallback(type, callback, allowDuplicates)
|
||||
}
|
||||
map.put("remove_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
entity.callbacks.removeCallback(type, callback)
|
||||
}
|
||||
map.put("has_callback") { params ->
|
||||
val type = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val callback = params.getString(1).asIdentifierDefaultingNamespace()
|
||||
return@put DoubleValue(callback in (entity.callbacks[type] ?: emptySet()))
|
||||
}
|
||||
map.put("clear_callbacks") { params ->
|
||||
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()
|
||||
if (type != null) {
|
||||
entity.callbacks[type]?.clear()
|
||||
} else {
|
||||
entity.callbacks.clear()
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
}
|
||||
|
||||
if (entity is PathfinderMob) {
|
||||
map.put("walk_to") { params ->
|
||||
val x = params.getDouble(0)
|
||||
@@ -877,6 +911,15 @@ object MoLangFunctions {
|
||||
map.put("is_sleeping") { _ -> DoubleValue(entity.isSleeping) }
|
||||
map.put("health") { _ -> DoubleValue(entity.health) }
|
||||
map.put("max_health") { _ -> DoubleValue(entity.maxHealth) }
|
||||
map.put("look_at_position") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
val z = params.getDouble(2)
|
||||
val duration = params.getIntOrNull(3) ?: 20
|
||||
val flags = params.params.subList(4, params.params.size).map { it.asString() }
|
||||
val brain = entity.brain
|
||||
brain.setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()), duration.toLong())
|
||||
}
|
||||
map.put("get_wander_control_memory") {
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
@@ -914,6 +957,22 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_uuid_memory") { params ->
|
||||
val memory = params.getString(0).asIdentifierDefaultingNamespace().let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<UUID>
|
||||
val uuid = params.getString(1).asUUID ?: return@put DoubleValue.ZERO
|
||||
val expiry = params.getIntOrNull(2) ?: -1
|
||||
if (expiry != -1) {
|
||||
entity.brain.setMemoryWithExpiry(memory, uuid, expiry.toLong())
|
||||
} else {
|
||||
entity.brain.setMemory(memory, uuid)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("erase_memory") { params ->
|
||||
val memories = params.params.map { it.asString().asIdentifierDefaultingNamespace() }.map(BuiltInRegistries.MEMORY_MODULE_TYPE::get)
|
||||
memories.forEach { entity.brain.eraseMemory(it) }
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
map.put("has_memory_value") { params ->
|
||||
val memoryTypes = params.params.map {
|
||||
it.asString().asIdentifierDefaultingNamespace()
|
||||
@@ -1232,6 +1291,23 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val battleMessageFunctions = mutableListOf<(BattleMessage) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ message ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("has_argument_at") { params ->
|
||||
val index = params.getInt(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
return@put DoubleValue(value == message.argumentAt(index))
|
||||
}
|
||||
map.put("has_argument") { params ->
|
||||
val argumentName = params.getString(0)
|
||||
val value = params.getStringOrNull(1) ?: ""
|
||||
return@put DoubleValue(value == message.optionalArgument(argumentName))
|
||||
}
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val pokemonFunctions = mutableListOf<(Pokemon) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
{ pokemon ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
@@ -2192,6 +2268,14 @@ object MoLangFunctions {
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addBattleMessageFunctions(battleMessage: BattleMessage): QueryStruct {
|
||||
val addedFunctions = battleMessageFunctions
|
||||
.flatMap { it.invoke(battleMessage).entries }
|
||||
.associate { it.key to it.value }
|
||||
functions.putAll(addedFunctions)
|
||||
return this
|
||||
}
|
||||
|
||||
fun QueryStruct.addPokemonEntityFunctions(pokemonEntity: PokemonEntity): QueryStruct {
|
||||
val addedFunctions = pokemonEntityFunctions
|
||||
.flatMap { it.invoke(pokemonEntity).entries }
|
||||
|
||||
@@ -196,6 +196,11 @@ class RidingController<Settings : RidingBehaviourSettings, State : RidingBehavio
|
||||
return behaviour.maxUpStep(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun getRideSounds(settings: Settings, state: State, vehicle: PokemonEntity): RideSoundSettingsList {
|
||||
if (!isActive(settings, state, vehicle)) return RideSoundSettingsList()
|
||||
return behaviour.getRideSounds(settings, state, vehicle)
|
||||
}
|
||||
|
||||
override fun createDefaultState(settings: Settings): State {
|
||||
return behaviour.createDefaultState(settings)
|
||||
}
|
||||
|
||||
@@ -382,7 +382,11 @@ class JetBehaviour : RidingBehaviour<JetSettings, JetState> {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun shouldRotatePlayerHead(settings: JetSettings, state: JetState, vehicle: PokemonEntity): Boolean {
|
||||
override fun shouldRotatePlayerHead(
|
||||
settings: JetSettings,
|
||||
state: JetState,
|
||||
vehicle: PokemonEntity
|
||||
): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -343,7 +343,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
state: BoatState,
|
||||
vehicle: PokemonEntity
|
||||
): RideSoundSettingsList {
|
||||
TODO("Not yet implemented")
|
||||
return settings.rideSounds
|
||||
}
|
||||
|
||||
override fun maxUpStep(settings: BoatSettings, state: BoatState, vehicle: PokemonEntity) = 0f
|
||||
@@ -354,6 +354,7 @@ class BoatBehaviour : RidingBehaviour<BoatSettings, BoatState> {
|
||||
|
||||
class BoatSettings : RidingBehaviourSettings {
|
||||
override val key = BoatBehaviour.KEY
|
||||
var rideSounds: RideSoundSettingsList = RideSoundSettingsList()
|
||||
|
||||
var terminalVelocity = "-2.0".asExpression()
|
||||
private set
|
||||
@@ -363,10 +364,12 @@ class BoatSettings : RidingBehaviourSettings {
|
||||
|
||||
override fun encode(buffer: RegistryFriendlyByteBuf) {
|
||||
buffer.writeResourceLocation(key)
|
||||
rideSounds.encode(buffer)
|
||||
buffer.writeExpression(terminalVelocity)
|
||||
}
|
||||
|
||||
override fun decode(buffer: RegistryFriendlyByteBuf) {
|
||||
rideSounds = RideSoundSettingsList.decode(buffer)
|
||||
terminalVelocity = buffer.readExpression()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
|
||||
package com.cobblemon.mod.common.api.storage
|
||||
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.pokemon.Pokemon
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
@@ -45,9 +44,6 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
override fun initialize() {
|
||||
pokemon.forEachIndexed { index, pokemon ->
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(this, BottomlessPosition(index)))
|
||||
pokemon.changeObservable.pipe(
|
||||
stopAfter { pokemon.storeCoordinates.get()?.store != this }
|
||||
).subscribe { storeChangeObservable.emit(Unit) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -116,4 +112,8 @@ open class BottomlessStore(override val uuid: UUID) : PokemonStore<BottomlessPos
|
||||
storeChangeObservable.emit(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPokemonChanged(pokemon: Pokemon) {
|
||||
this.storeChangeObservable.emit(Unit)
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ abstract class PokemonStore<T : StorePosition> : Iterable<Pokemon> {
|
||||
abstract fun getObservingPlayers(): Iterable<ServerPlayer>
|
||||
/** Sends the contents of this store to a player as if they've never seen it before. This initializes the store then sends each contained Pokémon. */
|
||||
abstract fun sendTo(player: ServerPlayer)
|
||||
/** Notifies the store that the provided Pokémon has changed in some way that would require persisting. */
|
||||
abstract fun onPokemonChanged(pokemon: Pokemon)
|
||||
|
||||
/**
|
||||
* Runs initialization logic for this store, knowing that it has just been constructed in a [PokemonStoreFactory].
|
||||
|
||||
@@ -11,7 +11,6 @@ package com.cobblemon.mod.common.api.storage.party
|
||||
import com.cobblemon.mod.common.CobblemonNetwork.sendPacket
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
|
||||
import com.cobblemon.mod.common.api.storage.PokemonStore
|
||||
@@ -62,22 +61,10 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
throw IllegalArgumentException("Slot position is out of bounds")
|
||||
} else {
|
||||
slots[position.slot] = pokemon
|
||||
if (pokemon != null) {
|
||||
if (pokemon.storeCoordinates.get()?.store != this) {
|
||||
// It's new to this store. Attach the listener
|
||||
trackPokemon(pokemon)
|
||||
}
|
||||
}
|
||||
anyChangeObservable.emit(Unit)
|
||||
}
|
||||
}
|
||||
|
||||
fun trackPokemon(pokemon: Pokemon) {
|
||||
pokemon.changeObservable
|
||||
.pipe(stopAfter { pokemon.storeCoordinates.get()?.store != this })
|
||||
.subscribe { anyChangeObservable.emit(Unit) }
|
||||
}
|
||||
|
||||
override fun getFirstAvailablePosition(): PartyPosition? {
|
||||
for (i in slots.indices) {
|
||||
if (slots[i] == null) {
|
||||
@@ -148,10 +135,13 @@ open class PartyStore(override val uuid: UUID) : PokemonStore<PartyPosition>() {
|
||||
for (slot in slots.indices) {
|
||||
val pokemon = get(slot) ?: continue
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(this, PartyPosition(slot)))
|
||||
trackPokemon(pokemon)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPokemonChanged(pokemon: Pokemon) {
|
||||
anyChangeObservable.emit(Unit)
|
||||
}
|
||||
|
||||
fun toGappyList() = slots.toList()
|
||||
|
||||
/** Maps the slots of the party using the giving mapper function, but preserving the nulls in the party at the right spots. */
|
||||
|
||||
@@ -62,14 +62,7 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
open operator fun set(index: Int, pokemon: Pokemon?) {
|
||||
if (index in 0 until POKEMON_PER_BOX) {
|
||||
this.pokemon[index] = pokemon
|
||||
if (pokemon != null) {
|
||||
val previousCoordinates = pokemon.storeCoordinates.get()
|
||||
val position = previousCoordinates?.position
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(pc, PCPosition(boxNumber, index)))
|
||||
if (previousCoordinates?.store !is PCStore || previousCoordinates.store.uuid != pc.uuid || (position as PCPosition).box != boxNumber) {
|
||||
trackPokemon(pokemon)
|
||||
}
|
||||
}
|
||||
pokemon?.storeCoordinates?.set(StoreCoordinates(pc, PCPosition(boxNumber, index)))
|
||||
if (emit) {
|
||||
boxChangeEmitter.emit(Unit)
|
||||
}
|
||||
@@ -97,7 +90,6 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
if (pokemon != null) {
|
||||
val position = PCPosition(box, slot)
|
||||
pokemon.storeCoordinates.set(StoreCoordinates(pc, position))
|
||||
trackPokemon(pokemon)
|
||||
}
|
||||
}
|
||||
boxChangeEmitter.subscribe { pc.pcChangeObservable.emit(Unit) }
|
||||
@@ -108,17 +100,7 @@ open class PCBox(val pc: PCStore) : Iterable<Pokemon> {
|
||||
pokemon.forEachIndexed { slot, pokemon ->
|
||||
pokemon?.storeCoordinates?.set(StoreCoordinates(pc, PCPosition(boxNumber, slot)))
|
||||
}
|
||||
}
|
||||
|
||||
fun trackPokemon(pokemon: Pokemon) {
|
||||
pokemon.changeObservable
|
||||
.pipe(
|
||||
stopAfter {
|
||||
val coordinates = it.storeCoordinates.get() ?: return@stopAfter true
|
||||
return@stopAfter coordinates.store !is PCStore || coordinates.store.uuid != pc.uuid || (coordinates.position as PCPosition).box != boxNumber
|
||||
}
|
||||
)
|
||||
.subscribe { boxChangeEmitter.emit(Unit) }
|
||||
boxChangeEmitter.emit(Unit)
|
||||
}
|
||||
|
||||
fun sendTo(player: ServerPlayer) {
|
||||
|
||||
@@ -284,6 +284,10 @@ open class PCStore(
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPokemonChanged(pokemon: Pokemon) {
|
||||
pcChangeObservable.emit(Unit)
|
||||
}
|
||||
|
||||
fun clearPC() {
|
||||
boxes.forEach { box ->
|
||||
box.getNonEmptySlots().forEach{
|
||||
|
||||
@@ -21,126 +21,126 @@ object ElementalTypes {
|
||||
val NORMAL = register(
|
||||
name = "Normal",
|
||||
displayName = Component.translatable("cobblemon.type.normal"),
|
||||
hue = 0xDDDDCF,
|
||||
hue = 0xE8E8DA,
|
||||
textureXMultiplier = 0
|
||||
)
|
||||
|
||||
val FIRE = register(
|
||||
name = "Fire",
|
||||
displayName = Component.translatable("cobblemon.type.fire"),
|
||||
hue = 0xE55C32,
|
||||
hue = 0xFF6E21,
|
||||
textureXMultiplier = 1
|
||||
)
|
||||
|
||||
val WATER = register(
|
||||
name = "Water",
|
||||
displayName = Component.translatable("cobblemon.type.water"),
|
||||
hue = 0x4A9BE8,
|
||||
hue = 0x3FA5FF,
|
||||
textureXMultiplier = 2
|
||||
)
|
||||
|
||||
val GRASS = register(
|
||||
name = "Grass",
|
||||
displayName = Component.translatable("cobblemon.type.grass"),
|
||||
hue = 0x4DBC3C,
|
||||
hue = 0x62D14F,
|
||||
textureXMultiplier = 3
|
||||
)
|
||||
|
||||
val ELECTRIC = register(
|
||||
name = "Electric",
|
||||
displayName = Component.translatable("cobblemon.type.electric"),
|
||||
hue = 0xEFD128,
|
||||
hue = 0xFFD314,
|
||||
textureXMultiplier = 4
|
||||
)
|
||||
|
||||
val ICE = register(
|
||||
name = "Ice",
|
||||
displayName = Component.translatable("cobblemon.type.ice"),
|
||||
hue = 0x6BC3EF,
|
||||
hue = 0x54F2F2,
|
||||
textureXMultiplier = 5
|
||||
)
|
||||
|
||||
val FIGHTING = register(
|
||||
name = "Fighting",
|
||||
displayName = Component.translatable("cobblemon.type.fighting"),
|
||||
hue = 0xC44C5C,
|
||||
hue = 0xEF565D,
|
||||
textureXMultiplier = 6
|
||||
)
|
||||
|
||||
val POISON = register(
|
||||
name = "Poison",
|
||||
displayName = Component.translatable("cobblemon.type.poison"),
|
||||
hue = 0xA24BD8,
|
||||
hue = 0xD651FF,
|
||||
textureXMultiplier = 7
|
||||
)
|
||||
|
||||
val GROUND = register(
|
||||
name = "Ground",
|
||||
displayName = Component.translatable("cobblemon.type.ground"),
|
||||
hue = 0xD89950,
|
||||
hue = 0xF4A453,
|
||||
textureXMultiplier = 8
|
||||
)
|
||||
|
||||
val FLYING = register(
|
||||
name = "Flying",
|
||||
displayName = Component.translatable("cobblemon.type.flying"),
|
||||
hue = 0xBCC1FF,
|
||||
hue = 0xB8B2FF,
|
||||
textureXMultiplier = 9
|
||||
)
|
||||
|
||||
val PSYCHIC = register(
|
||||
name = "Psychic",
|
||||
displayName = Component.translatable("cobblemon.type.psychic"),
|
||||
hue = 0xD86AD6,
|
||||
hue = 0xFF5E9E,
|
||||
textureXMultiplier = 10
|
||||
)
|
||||
|
||||
val BUG = register(
|
||||
name = "Bug",
|
||||
displayName = Component.translatable("cobblemon.type.bug"),
|
||||
hue = 0xA2C831,
|
||||
hue = 0xD3D319,
|
||||
textureXMultiplier = 11
|
||||
)
|
||||
|
||||
val ROCK = register(
|
||||
name = "Rock",
|
||||
displayName = Component.translatable("cobblemon.type.rock"),
|
||||
hue = 0xAA9666,
|
||||
hue = 0xB7A16E,
|
||||
textureXMultiplier = 12
|
||||
)
|
||||
|
||||
val GHOST = register(
|
||||
name = "Ghost",
|
||||
displayName = Component.translatable("cobblemon.type.ghost"),
|
||||
hue = 0x9572E5,
|
||||
hue = 0x9C80F7,
|
||||
textureXMultiplier = 13
|
||||
)
|
||||
|
||||
val DRAGON = register(
|
||||
name = "Dragon",
|
||||
displayName = Component.translatable("cobblemon.type.dragon"),
|
||||
hue = 0x535DE8,
|
||||
hue = 0x7580FF,
|
||||
textureXMultiplier = 14
|
||||
)
|
||||
|
||||
val DARK = register(
|
||||
name = "Dark",
|
||||
displayName = Component.translatable("cobblemon.type.dark"),
|
||||
hue = 0x5C6CB2,
|
||||
hue = 0x587DA0,
|
||||
textureXMultiplier = 15
|
||||
)
|
||||
|
||||
val STEEL = register(
|
||||
name = "Steel",
|
||||
displayName = Component.translatable("cobblemon.type.steel"),
|
||||
hue = 0xC3CCE0,
|
||||
hue = 0xABD1F4,
|
||||
textureXMultiplier = 16
|
||||
)
|
||||
|
||||
val FAIRY = register(
|
||||
name = "Fairy",
|
||||
displayName = Component.translatable("cobblemon.type.fairy"),
|
||||
hue = 0xEA727E,
|
||||
hue = 0xFF7FE5,
|
||||
textureXMultiplier = 17
|
||||
)
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addBattleMessageFunctions
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
@@ -41,6 +42,11 @@ class ActivateInstruction(val instructionSet: InstructionSet, val message: Battl
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("activate")
|
||||
|
||||
override fun addMolangQueries(runtime: MoLangRuntime) {
|
||||
super.addMolangQueries(runtime)
|
||||
runtime.environment.query.addBattleMessageFunctions(message)
|
||||
}
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val effect = message.effectAt(1) ?: return
|
||||
|
||||
@@ -13,6 +13,7 @@ import com.cobblemon.mod.common.api.battles.interpreter.BattleContext
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.InvalidInstructionException
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addBattleMessageFunctions
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
@@ -43,6 +44,11 @@ class BoostInstruction(battle: PokemonBattle, val message: BattleMessage, val is
|
||||
val stages = message.argumentAt(2)?.toInt() ?: throw InvalidInstructionException(message)
|
||||
val stat = Stats.getStat(statKey).displayName
|
||||
|
||||
override fun addMolangQueries(runtime: MoLangRuntime) {
|
||||
super.addMolangQueries(runtime)
|
||||
runtime.environment.query.addBattleMessageFunctions(message)
|
||||
}
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addBattleMessageFunctions
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
@@ -35,6 +36,12 @@ class CantInstruction(val message: BattleMessage): ActionEffectInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("cant")
|
||||
|
||||
override fun addMolangQueries(runtime: MoLangRuntime) {
|
||||
super.addMolangQueries(runtime)
|
||||
runtime.environment.query.addBattleMessageFunctions(message)
|
||||
}
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@ import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.net.messages.client.animation.PlayPosableAnimationPacket
|
||||
import com.cobblemon.mod.common.net.messages.client.battle.BattleHealthChangePacket
|
||||
import com.cobblemon.mod.common.net.messages.client.effect.RunPosableMoLangPacket
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.DamageTakenEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.RecoilEvolutionProgress
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
|
||||
@@ -103,10 +102,18 @@ class DamageInstruction(
|
||||
status = pokemon.effectedPokemon.status?.status ?: status
|
||||
}
|
||||
|
||||
// Damage effects without a action effect receive the generic damage effect
|
||||
val actionEffect = status?.getActionEffect() ?: effect?.let {
|
||||
ActionEffects.actionEffects["generic_damage".asIdentifierDefaultingNamespace()]
|
||||
} ?: return@dispatch GO
|
||||
// Damage inflicted by an effect (not just a move) will potentially get its own action effect.
|
||||
// We don't play an effect at all if there's no effect on the damage because we anticipate that
|
||||
// the move's action effect will handle that. Status action effects are specially defined (we could
|
||||
// migrate this back to damage_{status} later tbh) but then it will try damage_{status} and failing
|
||||
// that then we should try to show something about the fact that some kind of effect damage is being applied.
|
||||
val actionEffect = status?.getActionEffect()
|
||||
?: effect?.let {
|
||||
ActionEffects.actionEffects["damage_${it.id}".asIdentifierDefaultingNamespace()]
|
||||
?: ActionEffects.actionEffects["generic_damage".asIdentifierDefaultingNamespace()]
|
||||
}
|
||||
?: return@dispatch GO // will occur if the damage is from a move
|
||||
|
||||
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
battlePokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
@@ -210,7 +217,7 @@ class DamageInstruction(
|
||||
} else if (causedFaint) {
|
||||
GO
|
||||
} else {
|
||||
UntilDispatch { "effects" !in holds}
|
||||
UntilDispatch { "effects" !in holds }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,11 +21,11 @@ import com.cobblemon.mod.common.util.battleLang
|
||||
* @since December 30th, 2022
|
||||
*/
|
||||
class HitCountInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
|
||||
val hitCount = message.argumentAt(1)?.toIntOrNull()
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchGo {
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return@dispatchGo
|
||||
val hitCount = message.argumentAt(1)?.toIntOrNull() ?: return@dispatchGo
|
||||
val hitCount = hitCount ?: return@dispatchGo
|
||||
val lang = if (hitCount == 1) battleLang("hit_count_singular") else battleLang("hit_count", hitCount)
|
||||
battle.minorBattleActions[battlePokemon.uuid] = message
|
||||
battle.broadcastChatMessage(lang)
|
||||
|
||||
@@ -15,7 +15,9 @@ import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.Effect
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addBattleMessageFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.moves.Moves
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.TargetsProvider
|
||||
@@ -28,9 +30,11 @@ import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.battles.pokemon.BattlePokemon
|
||||
import com.cobblemon.mod.common.pokemon.evolution.progress.UseMoveEvolutionProgress
|
||||
import com.cobblemon.mod.common.util.asArrayValue
|
||||
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
|
||||
@@ -93,15 +97,27 @@ class MoveInstruction(
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.majorBattleActions[userPokemon.uuid] = message
|
||||
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
userPokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
if (spreadTargetPokemon.isNotEmpty()) {
|
||||
providers.add(TargetsProvider(spreadTargetPokemon.filter { it?.effectedPokemon?.entity != null}.mapNotNull { spreadTarget -> spreadTarget?.effectedPokemon?.entity }))
|
||||
} else {
|
||||
targetPokemon?.effectedPokemon?.entity?.let { TargetsProvider(it) }?.let(providers::add)
|
||||
}
|
||||
val runtime = MoLangRuntime().also {
|
||||
battle.addQueryFunctions(it.environment.query).addStandardFunctions()
|
||||
it.environment.query.addBattleMessageFunctions(message)
|
||||
}
|
||||
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
userPokemon.effectedPokemon.entity?.let { entity ->
|
||||
runtime.environment.query.addFunction("user") { entity.asMostSpecificMoLangValue()}
|
||||
UsersProvider(entity)
|
||||
}?.let(providers::add)
|
||||
|
||||
if (spreadTargetPokemon.isNotEmpty()) {
|
||||
val targetEntities = spreadTargetPokemon.mapNotNull { it?.effectedPokemon?.entity }
|
||||
runtime.environment.query.addFunction("targets") { targetEntities.asArrayValue { it.asMostSpecificMoLangValue() } }
|
||||
providers.add(TargetsProvider(targetEntities))
|
||||
} else {
|
||||
targetPokemon?.effectedPokemon?.entity?.let { entity ->
|
||||
runtime.environment.query.addFunction("target") { entity.asMostSpecificMoLangValue() }
|
||||
val provider = TargetsProvider(entity)
|
||||
providers.add(provider)
|
||||
}
|
||||
}
|
||||
|
||||
actionEffect ?: return@dispatch GO
|
||||
@@ -114,6 +130,7 @@ class MoveInstruction(
|
||||
|
||||
val subsequentInstructions = instructionSet.findInstructionsCausedBy(this)
|
||||
val missedTargets = subsequentInstructions.filterIsInstance<MissInstruction>().mapNotNull { it.target }
|
||||
val hitCountInstruction = subsequentInstructions.filterIsInstance<HitCountInstruction>().firstOrNull()
|
||||
|
||||
runtime.environment.query.addFunction("missed") { params ->
|
||||
if (params.params.size == 0) {
|
||||
@@ -134,6 +151,10 @@ class MoveInstruction(
|
||||
}
|
||||
}
|
||||
|
||||
if (hitCountInstruction != null && hitCountInstruction.hitCount != null) {
|
||||
runtime.environment.query.addFunction("hit_count") { DoubleValue(hitCountInstruction.hitCount.toDouble()) }
|
||||
}
|
||||
|
||||
runtime.environment.query.addFunction("move") { move.struct }
|
||||
runtime.environment.query.addFunction("instruction_id") { StringValue(cobblemonResource("move").toString()) }
|
||||
|
||||
|
||||
@@ -8,10 +8,26 @@
|
||||
|
||||
package com.cobblemon.mod.common.battles.interpreter.instructions
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.cobblemon.mod.common.api.battles.interpreter.BattleMessage
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addBattleMessageFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addFunctions
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffects
|
||||
import com.cobblemon.mod.common.api.moves.animations.UsersProvider
|
||||
import com.cobblemon.mod.common.api.pokemon.status.Statuses
|
||||
import com.cobblemon.mod.common.battles.ShowdownInterpreter
|
||||
import com.cobblemon.mod.common.battles.dispatch.ActionEffectInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.GO
|
||||
import com.cobblemon.mod.common.battles.dispatch.InterpreterInstruction
|
||||
import com.cobblemon.mod.common.battles.dispatch.UntilDispatch
|
||||
import com.cobblemon.mod.common.pokemon.status.statuses.persistent.PoisonStatus
|
||||
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.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Format: |-prepare|ATTACKER|MOVE and |-prepare|ATTACKER|MOVE|DEFENDER
|
||||
@@ -20,13 +36,56 @@ import com.cobblemon.mod.common.util.battleLang
|
||||
* @author Renaissance
|
||||
* @since March 24th, 2023
|
||||
*/
|
||||
class PrepareInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
class PrepareInstruction(val message: BattleMessage): ActionEffectInstruction {
|
||||
override var future: CompletableFuture<*> = CompletableFuture.completedFuture(Unit)
|
||||
override var holds = mutableSetOf<String>()
|
||||
override val id = cobblemonResource("prepare")
|
||||
|
||||
override fun invoke(battle: PokemonBattle) {
|
||||
battle.dispatchWaiting(1.5F) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatchWaiting
|
||||
override fun addMolangQueries(runtime: MoLangRuntime) {
|
||||
super.addMolangQueries(runtime)
|
||||
runtime.environment.query.addBattleMessageFunctions(message)
|
||||
}
|
||||
|
||||
override fun preActionEffect(battle: PokemonBattle) {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return
|
||||
val effect = message.effectAt(1) ?: return
|
||||
ShowdownInterpreter.broadcastOptionalAbility(battle, effect, pokemon)
|
||||
|
||||
battle.dispatch{
|
||||
ShowdownInterpreter.lastCauser[battle.battleId] = message
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun runActionEffect(battle: PokemonBattle, runtime: MoLangRuntime) {
|
||||
val effect = message.effectAt(1)
|
||||
val battlePokemon = message.battlePokemon(0, battle) ?: return
|
||||
battle.dispatch {
|
||||
val actionEffect = effect?.let { ActionEffects.actionEffects["prepare_${it.id}".asIdentifierDefaultingNamespace()] }
|
||||
?: return@dispatch GO // not likely
|
||||
|
||||
val providers = mutableListOf<Any>(battle)
|
||||
battlePokemon.effectedPokemon.entity?.let { UsersProvider(it) }?.let(providers::add)
|
||||
|
||||
val context = ActionEffectContext(
|
||||
actionEffect = actionEffect,
|
||||
runtime = runtime,
|
||||
providers = providers,
|
||||
level = battle.players.firstOrNull()?.level()
|
||||
)
|
||||
this.future = actionEffect.run(context)
|
||||
holds = context.holds // Reference so future things can check on this action effect's holds
|
||||
future.thenApply { holds.clear() }
|
||||
return@dispatch GO
|
||||
}
|
||||
}
|
||||
|
||||
override fun postActionEffect(battle: PokemonBattle) {
|
||||
battle.dispatch {
|
||||
val pokemon = message.battlePokemon(0, battle) ?: return@dispatch GO
|
||||
val pokemonName = pokemon.getName()
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatchWaiting
|
||||
val effectID = message.effectAt(1)?.id ?: return@dispatch GO
|
||||
//Prevents spam when the move Role Play is used
|
||||
val lang = when (effectID) {
|
||||
"shadowforce" -> battleLang("prepare.phantomforce", pokemonName) //Phantom Force and Shadow Force share the same text
|
||||
@@ -35,6 +94,7 @@ class PrepareInstruction(val message: BattleMessage): InterpreterInstruction {
|
||||
}
|
||||
battle.broadcastChatMessage(lang)
|
||||
battle.minorBattleActions[pokemon.uuid] = message
|
||||
UntilDispatch { "effects" !in holds }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,6 @@ import com.cobblemon.mod.common.CobblemonNetwork.sendToServer
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.gui.blitk
|
||||
import com.cobblemon.mod.common.api.moves.Move
|
||||
import com.cobblemon.mod.common.api.reactive.Observable.Companion.stopAfter
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.storage.party.PartyPosition
|
||||
@@ -25,7 +24,11 @@ import com.cobblemon.mod.common.client.CobblemonResources
|
||||
import com.cobblemon.mod.common.client.gui.CobblemonRenderable
|
||||
import com.cobblemon.mod.common.client.gui.ExitButton
|
||||
import com.cobblemon.mod.common.client.gui.TypeIcon
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.*
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.EvolutionSelectScreen
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.MarkingsWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.ModelWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.NicknameEntryWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.PartyWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.SummaryTab
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.info.InfoWidget
|
||||
import com.cobblemon.mod.common.client.gui.summary.widgets.screens.marks.MarksWidget
|
||||
@@ -328,9 +331,6 @@ class Summary private constructor(party: Collection<Pokemon?>, private val edita
|
||||
saveMarkings()
|
||||
this.selectedPokemon.moveSet.changeFunction = {}
|
||||
this.party.getOrNull(newSelection)?.let { newPokemon ->
|
||||
newPokemon.changeObservable.pipe( stopAfter { Minecraft.getInstance().screen != this || this.selectedPokemon != newPokemon } ).subscribe {
|
||||
updatePokemonInfo()
|
||||
}
|
||||
this.selectedPokemon = newPokemon
|
||||
}
|
||||
updatePokemonInfo()
|
||||
|
||||
@@ -85,7 +85,7 @@ object StarterDataLoader : JsonDataRegistry<StarterCategory> {
|
||||
}
|
||||
}
|
||||
|
||||
observable.emit()
|
||||
observable.emit(this)
|
||||
}
|
||||
|
||||
override fun sync(player: ServerPlayer) {}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.entity
|
||||
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonBehaviour
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.scripting.CobblemonScripts
|
||||
import com.cobblemon.mod.common.util.asIdentifierDefaultingNamespace
|
||||
import com.cobblemon.mod.common.util.cobblemonResource
|
||||
import com.cobblemon.mod.common.util.resolve
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
|
||||
/**
|
||||
* A class that holds callbacks for an entity, allowing scripts to be associated with specific events.
|
||||
* Mainly keeps code bloat away from the entity classes. There's likely to be some overlap between these
|
||||
* and the datapacked MoLang callbacks but the difference is that these are per-entity which allows
|
||||
* [CobblemonBehaviour]s to add callbacks very gracefully, rather than running the same code for all
|
||||
* entities then looking for some other property on the entity.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 26th, 2025
|
||||
*/
|
||||
class EntityCallbacks(val entity: Entity) : HashMap<ResourceLocation, MutableList<ResourceLocation>>() {
|
||||
companion object {
|
||||
val HIT_BY_POKEBALL = cobblemonResource("hit_by_pokeball")
|
||||
val SAW_ENTITY = cobblemonResource("saw_entity")
|
||||
val HURT = cobblemonResource("hurt")
|
||||
val DIED = cobblemonResource("died")
|
||||
}
|
||||
|
||||
val runtime = MoLangRuntime().setup().also {
|
||||
it.environment.query.addFunction("entity") { entity.asMostSpecificMoLangValue() }
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a script to the callbacks for a specific type. If `allowDuplicates` is false, it will not add
|
||||
* the script if it already exists in the list for that type.
|
||||
*
|
||||
* Returns true if the script was added, false if it was not added due to a duplicate being found and respected.
|
||||
*/
|
||||
fun addCallback(type: ResourceLocation, callback: ResourceLocation, allowDuplicates: Boolean): Boolean {
|
||||
val callbacks = this.getOrPut(type) { mutableListOf() }
|
||||
if (!allowDuplicates && callback in callbacks) {
|
||||
return false
|
||||
}
|
||||
callbacks.add(callback)
|
||||
return true
|
||||
}
|
||||
|
||||
fun removeCallback(type: ResourceLocation, callback: ResourceLocation): Boolean {
|
||||
val callbacks = this[type] ?: return false
|
||||
return callbacks.remove(callback)
|
||||
}
|
||||
|
||||
fun process(type: ResourceLocation, functions: Map<String, (MoParams) -> MoValue> = hashMapOf()) {
|
||||
if (type !in this) {
|
||||
return
|
||||
}
|
||||
functions.forEach { (name, function) -> runtime.environment.query.addFunction(name, function) }
|
||||
val callbacks = this[type]?.mapNotNull { CobblemonScripts.scripts[it] } ?: return
|
||||
for (callback in callbacks) {
|
||||
runtime.resolve(callback)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,11 +17,11 @@ import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
|
||||
import com.cobblemon.mod.common.entity.npc.NPCEntity
|
||||
import com.cobblemon.mod.common.util.DataKeys
|
||||
import com.mojang.serialization.Dynamic
|
||||
import com.mojang.serialization.DynamicOps
|
||||
import net.minecraft.nbt.CompoundTag
|
||||
import net.minecraft.nbt.ListTag
|
||||
import net.minecraft.nbt.StringTag
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
import net.minecraft.world.entity.Entity
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.Brain
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
@@ -40,6 +40,7 @@ interface MoLangScriptingEntity {
|
||||
val registeredVariables: MutableList<MoLangConfigVariable>
|
||||
var config: VariableStruct
|
||||
var data: VariableStruct
|
||||
var callbacks: EntityCallbacks
|
||||
|
||||
fun getExtraVariables(): List<MoLangConfigVariable> = emptyList()
|
||||
fun remakeBrain()
|
||||
@@ -100,7 +101,7 @@ interface MoLangScriptingEntity {
|
||||
fun updateBehaviours(behaviours: Collection<ResourceLocation>) {
|
||||
val removingBehaviours = this@MoLangScriptingEntity.behaviours.filterNot(behaviours::contains).mapNotNull(CobblemonBehaviours.behaviours::get)
|
||||
removingBehaviours.forEach { behaviour ->
|
||||
behaviour.undo(this as LivingEntity)
|
||||
behaviour.onRemove(this as LivingEntity)
|
||||
}
|
||||
this@MoLangScriptingEntity.behaviours.clear()
|
||||
this@MoLangScriptingEntity.behaviours.addAll(behaviours)
|
||||
|
||||
@@ -38,6 +38,7 @@ import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.storage.party.NPCPartyStore
|
||||
import com.cobblemon.mod.common.api.text.text
|
||||
import com.cobblemon.mod.common.entity.BehaviourEditingTracker
|
||||
import com.cobblemon.mod.common.entity.EntityCallbacks
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.entity.OmniPathingEntity
|
||||
import com.cobblemon.mod.common.entity.PosableEntity
|
||||
@@ -242,6 +243,7 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
override val registeredVariables: MutableList<MoLangConfigVariable> = mutableListOf()
|
||||
override var data = VariableStruct()
|
||||
override var config = VariableStruct()
|
||||
override var callbacks = EntityCallbacks(this)
|
||||
|
||||
val aspects: Set<String>
|
||||
get() = entityData.get(ASPECTS)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.entity.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.Cobblemon
|
||||
import com.cobblemon.mod.common.CobblemonEntities.EMPTY_POKEBALL
|
||||
@@ -17,6 +18,7 @@ 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.asMostSpecificMoLangValue
|
||||
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
|
||||
@@ -120,6 +122,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
.addFunction("capture_state") { StringValue(captureState.name) }
|
||||
.addFunction("ball_type") { StringValue(pokeBall.name.toString()) }
|
||||
.addFunction("aspects") { aspects.asArrayValue { StringValue(it) } }
|
||||
.addFunction("thrower") { owner?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO }
|
||||
|
||||
override fun defineSynchedData(builder: SynchedEntityData.Builder) {
|
||||
pokeBall = PokeBalls.POKE_BALL
|
||||
@@ -445,14 +448,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
level().playSoundServer(position(), CobblemonSounds.POKE_BALL_HIT, volume = 1F)
|
||||
|
||||
// Hit Pokémon plays recoil animation
|
||||
val pkt = PlayPosableAnimationPacket(pokemonEntity.id, setOf("recoil"), emptyList())
|
||||
pkt.sendToPlayersAround(
|
||||
x = pokemonEntity.x,
|
||||
y = pokemonEntity.y,
|
||||
z = pokemonEntity.z,
|
||||
worldKey = pokemonEntity.level().dimension(),
|
||||
distance = 50.0
|
||||
)
|
||||
pokemonEntity.playAnimation("recoil")
|
||||
|
||||
// Bounce backwards away from the hit Pokémon
|
||||
deltaMovement = displace.multiply(-1.0, 0.0, -1.0).normalize().yRot(mul * PI/3).multiply(0.1, 0.0, 0.1).add(0.0, 1.0 / 3, 0.0)
|
||||
|
||||
@@ -386,6 +386,7 @@ open class PokemonEntity(
|
||||
override val registeredVariables = mutableListOf<MoLangConfigVariable>()
|
||||
override var config = VariableStruct()
|
||||
override var data = VariableStruct()
|
||||
override var callbacks = EntityCallbacks(this)
|
||||
|
||||
var platform: PlatformType
|
||||
get() = entityData.get(PLATFORM_TYPE)
|
||||
@@ -564,10 +565,7 @@ open class PokemonEntity(
|
||||
isPokemonFlying = flyDist - flyDistO > 0.005F
|
||||
isPokemonWalking = walkDist - walkDistO > 0.005F
|
||||
|
||||
entityData.get(MOVING)
|
||||
|
||||
// Update ride Specific data
|
||||
if (level().isClientSide) {
|
||||
if (passengers.isNotEmpty() && level().isClientSide) {
|
||||
rideSoundManager.tick()
|
||||
ridingAnimationData.update()
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common.entity.pokemon.ai.tasks
|
||||
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonBlockPosTracker
|
||||
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
|
||||
import net.minecraft.world.entity.ai.behavior.EntityTracker
|
||||
import net.minecraft.world.entity.ai.behavior.OneShot
|
||||
@@ -33,11 +34,16 @@ object LookAtTargetedBattlePokemonTask {
|
||||
Trigger { world, entity, _ ->
|
||||
val targeted = it.tryGet(targetedBattlePokemon).orElse(null)
|
||||
val targetedEntity = targeted?.let { world.getEntity(it) as? PokemonEntity }
|
||||
val look = it.tryGet(lookTarget).orElse(null) as? EntityTracker
|
||||
var look = it.tryGet(lookTarget).orElse(null)
|
||||
if (look is CobblemonBlockPosTracker && look.isBattleLookBypass()) {
|
||||
return@Trigger false
|
||||
} else if (look !is EntityTracker) {
|
||||
look = null
|
||||
}
|
||||
if (targeted != null && targetedEntity == null) {
|
||||
entity.brain.eraseMemory(CobblemonMemories.TARGETED_BATTLE_POKEMON)
|
||||
return@Trigger false
|
||||
} else if (targetedEntity != null && look?.entity != targetedEntity) {
|
||||
} else if (targetedEntity != null && (look as? EntityTracker)?.entity != targetedEntity) {
|
||||
entity.brain.setMemory(MemoryModuleType.LOOK_TARGET, EntityTracker(targetedEntity, true))
|
||||
return@Trigger true
|
||||
} else if (targetedEntity == null) {
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.events
|
||||
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.value.MoValue
|
||||
import com.cobblemon.mod.common.api.Priority
|
||||
import com.cobblemon.mod.common.api.events.CobblemonEvents.THROWN_POKEBALL_HIT
|
||||
import com.cobblemon.mod.common.api.events.pokeball.ThrownPokeballHitEvent
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.entity.EntityCallbacks
|
||||
import com.cobblemon.mod.common.entity.EntityCallbacks.Companion.HIT_BY_POKEBALL
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import net.minecraft.resources.ResourceLocation
|
||||
|
||||
/**
|
||||
* Handles the registration of entity callbacks for events related to entities. You can add your own
|
||||
* as well, all this actually does is subscribe to the event and pass it through the [EntityCallbacks] of
|
||||
* the entity the event is about. In general, entity callbacks occur on the normal priority so that
|
||||
* plugins can jump in ahead but on parity with the general MoLang callbacks.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since July 26th, 2025
|
||||
*/
|
||||
object EntityCallbackHandler {
|
||||
fun setup() {
|
||||
bindCallback(THROWN_POKEBALL_HIT, HIT_BY_POKEBALL, ThrownPokeballHitEvent::pokemon, ThrownPokeballHitEvent::functions)
|
||||
}
|
||||
|
||||
fun <T> bindCallback(
|
||||
observable: Observable<T>,
|
||||
type: ResourceLocation,
|
||||
entity: (T) -> MoLangScriptingEntity,
|
||||
functions: (T) -> Map<String, (MoParams) -> MoValue>
|
||||
) {
|
||||
observable.subscribe(priority = Priority.NORMAL) { entity(it).callbacks.process(type, functions(it)) }
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,7 @@ object FlowHandler {
|
||||
CobblemonEvents.BATTLE_STARTED_POST.subscribe { CobblemonFlows.run(cobblemonResource("battle_started_post"), it.context) }
|
||||
CobblemonEvents.APRICORN_HARVESTED.subscribe { CobblemonFlows.run(cobblemonResource("apricorn_harvested"), it.context) }
|
||||
CobblemonEvents.BERRY_HARVEST.subscribe { CobblemonFlows.run(cobblemonResource("berry_harvested"), it.context) }
|
||||
CobblemonEvents.THROWN_POKEBALL_HIT.subscribe { CobblemonFlows.run(cobblemonResource("thrown_pokeball_hit"), it.context) }
|
||||
CobblemonEvents.THROWN_POKEBALL_HIT.subscribe { CobblemonFlows.run(cobblemonResource("thrown_pokeball_hit"), emptyMap(), functions = it.functions) }
|
||||
CobblemonEvents.POKEMON_HEALED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_healed"), it.context, it.functions) }
|
||||
CobblemonEvents.POKEMON_ASPECTS_CHANGED.subscribe { CobblemonFlows.run(cobblemonResource("pokemon_aspects_changed"), it.context) }
|
||||
CobblemonEvents.LEVEL_UP_EVENT.subscribe { CobblemonFlows.run(cobblemonResource("level_up"), it.context, it.functions) }
|
||||
|
||||
@@ -56,9 +56,7 @@ import com.cobblemon.mod.common.api.pokemon.moves.LearnsetQuery
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stat
|
||||
import com.cobblemon.mod.common.api.pokemon.stats.Stats
|
||||
import com.cobblemon.mod.common.api.properties.CustomPokemonProperty
|
||||
import com.cobblemon.mod.common.api.reactive.Observable
|
||||
import com.cobblemon.mod.common.api.reactive.SettableObservable
|
||||
import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.riding.RidingProperties
|
||||
import com.cobblemon.mod.common.api.riding.stats.RidingStat
|
||||
import com.cobblemon.mod.common.api.scheduling.afterOnServer
|
||||
@@ -625,7 +623,7 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
|
||||
/**
|
||||
* Arbitrary data compound. Be aware that updating this is not enough for a Pokémon to be recognized as dirty
|
||||
* and in need of saving. Emit to [changeObservable] if you are making a change otherwise you'll see reversions.
|
||||
* and in need of saving. Call [onChange] if you are making a change otherwise you'll potentially see reversions.
|
||||
*/
|
||||
var persistentData: CompoundTag =
|
||||
CompoundTag()
|
||||
@@ -2023,20 +2021,18 @@ open class Pokemon : ShowdownIdentifiable {
|
||||
onChange(packet)
|
||||
}
|
||||
|
||||
/** An [Observable] that emits the Pokémon whenever any change is made to it. The change itself is not included. */
|
||||
val changeObservable = SimpleObservable<Pokemon>()
|
||||
|
||||
/**
|
||||
* Function to run when a save-able change has been made to the Pokémon. This takes a packet to send to watching
|
||||
* players just for convenience, but the main thing is that this will push an update to [changeObservable] which
|
||||
* is primarily (for our purposes, at least) so that we can detect when storage needs to be queued for saving.
|
||||
* players just for convenience, but the main thing is that this will notify the store that this Pokémon is in
|
||||
* (if it's in a party/PC/whatever) that the Pokémon has changed and that it should consider saving it when it can.
|
||||
*/
|
||||
fun onChange(packet: PokemonUpdatePacket<*>? = null) {
|
||||
if (packet != null && storeCoordinates.get() != null) {
|
||||
val storeCoordinates = storeCoordinates.get() ?: return // If they aren't in a store then we don't care.
|
||||
if (packet != null) {
|
||||
notify(packet)
|
||||
}
|
||||
|
||||
changeObservable.emit(this)
|
||||
storeCoordinates.store.onPokemonChanged(pokemon = this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -3727,6 +3727,15 @@
|
||||
"cobblemon.behaviour.npc_scale_configuration.name": "NPC Entity Scales",
|
||||
"cobblemon.behaviour.npc_scale_configuration.desc": "Allows configuring the render- and hitbox-scale of the NPC.",
|
||||
|
||||
"cobblemon.behaviour.pokemon_gets_mad_at_thrower.name": "Gets Mad At Poké Ball Thrower",
|
||||
"cobblemon.behaviour.pokemon_gets_mad_at_thrower.desc": "Gets angry at the player that threw a Poké Ball at it. Aggressive Pokémon will attack the player, while non-aggressive Pokémon will flee.",
|
||||
|
||||
"cobblemon.behaviour.attack_hostile_mobs.name": "Attacks Hostile Mobs",
|
||||
"cobblemon.behaviour.attack_hostile_mobs.desc": "Attacks hostile mobs that are nearby, even if unprovoked.",
|
||||
|
||||
"cobblemon.behaviour.pokemon_bee.name": "Pollinates Flowers",
|
||||
"cobblemon.behaviour.pokemon_bee.desc": "Pollinates flowers and adds honey to hives and Saccharine tree leaves.",
|
||||
|
||||
"npc.sacchi.name": "Professor Sacchi",
|
||||
|
||||
"cobblemon.move.pound": "Pound",
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/mental_herb_stage_0",
|
||||
"particle": "cobblemon:block/crops/mental_herb_stage_0"
|
||||
"0": "cobblemon:block/crops/mental_herb_stage_0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/mental_herb_stage_1",
|
||||
"particle": "cobblemon:block/crops/mental_herb_stage_1"
|
||||
"0": "cobblemon:block/crops/mental_herb_stage_1"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/mirror_herb_stage_0",
|
||||
"particle": "cobblemon:block/crops/mirror_herb_stage_0"
|
||||
"0": "cobblemon:block/crops/mirror_herb_stage_0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/mirror_herb_stage_1",
|
||||
"particle": "cobblemon:block/crops/mirror_herb_stage_1"
|
||||
"0": "cobblemon:block/crops/mirror_herb_stage_1"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/power_herb_stage_0",
|
||||
"particle": "cobblemon:block/crops/power_herb_stage_0"
|
||||
"0": "cobblemon:block/crops/power_herb_stage_0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/power_herb_stage_1",
|
||||
"particle": "cobblemon:block/crops/power_herb_stage_1"
|
||||
"0": "cobblemon:block/crops/power_herb_stage_1"
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
{
|
||||
"credit": "Made with Blockbench / EikoBiko",
|
||||
"texture_size": [64, 64],
|
||||
"format_version": "1.21.6",
|
||||
"credit": "Made with Blockbench",
|
||||
"texture_size": [32, 16],
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_0",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_0"
|
||||
"particle": "#0"
|
||||
},
|
||||
"elements": [
|
||||
{
|
||||
@@ -11,12 +11,8 @@
|
||||
"to": [15, 9, 8],
|
||||
"rotation": {"angle": -45, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 4, 7, 9], "texture": "#0"},
|
||||
"east": {"uv": [3, 4.5, 3, 4.5], "texture": "#0"},
|
||||
"south": {"uv": [0, 4, 7, 9], "texture": "#0"},
|
||||
"west": {"uv": [3, 4.5, 3, 4.5], "texture": "#0"},
|
||||
"up": {"uv": [8, 0, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [8, 0, 0, 0], "texture": "#0"}
|
||||
"north": {"uv": [0, 0, 7, 10], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 7, 10], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -24,25 +20,17 @@
|
||||
"to": [8, 9, 15],
|
||||
"rotation": {"angle": -45, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [3, 4.5, 3, 4.5], "texture": "#0"},
|
||||
"east": {"uv": [0, 4, 7, 9], "texture": "#0"},
|
||||
"south": {"uv": [3, 4.5, 3, 4.5], "texture": "#0"},
|
||||
"west": {"uv": [0, 4, 7, 9], "texture": "#0"},
|
||||
"up": {"uv": [0, 8, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [0, 0, 0, 8], "texture": "#0"}
|
||||
"east": {"uv": [0, 0, 7, 10], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 7, 10], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [1, -1, 8],
|
||||
"to": [15, 7, 8],
|
||||
"from": [4, 1, 8],
|
||||
"to": [12, 5, 8],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 7, 4], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 0, 3.5], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 7, 4], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 0, 3.5], "texture": "#0"},
|
||||
"up": {"uv": [2, 0, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [2, 0, 0, 0], "texture": "#0"}
|
||||
"north": {"uv": [0, 11, 4, 15], "texture": "#0"},
|
||||
"south": {"uv": [0, 11, 4, 15], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -50,25 +38,17 @@
|
||||
"to": [10, 11, 8],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [14, 1.5, 16, 4.5], "texture": "#0"},
|
||||
"east": {"uv": [0, 0, 0, 3.5], "texture": "#0"},
|
||||
"south": {"uv": [14, 1.5, 16, 4.5], "texture": "#0"},
|
||||
"west": {"uv": [0, 0, 0, 3.5], "texture": "#0"},
|
||||
"up": {"uv": [2, 0, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [2, 0, 0, 0], "texture": "#0"}
|
||||
"north": {"uv": [11, 5, 13, 11], "texture": "#0"},
|
||||
"south": {"uv": [11, 5, 13, 11], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
"from": [8, -1, 1],
|
||||
"to": [8, 14, 15],
|
||||
"from": [8, 1, 4],
|
||||
"to": [8, 14, 12],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 0, 0, 8.5], "texture": "#0"},
|
||||
"east": {"uv": [7, 0, 14, 7.5], "texture": "#0"},
|
||||
"south": {"uv": [0, 0, 0, 8.5], "texture": "#0"},
|
||||
"west": {"uv": [14, 0, 7, 7.5], "texture": "#0"},
|
||||
"up": {"uv": [0, 6, 0, 0], "texture": "#0"},
|
||||
"down": {"uv": [0, 0, 0, 6], "texture": "#0"}
|
||||
"east": {"uv": [7, 2, 11, 15], "texture": "#0"},
|
||||
"west": {"uv": [11, 2, 7, 15], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -76,12 +56,8 @@
|
||||
"to": [11, 16, 11],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"south": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"west": {"uv": [3, 13, 0, 16], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 3, 13], "texture": "#0"},
|
||||
"down": {"uv": [0, 13, 3, 16], "texture": "#0"}
|
||||
"up": {"uv": [13, 6, 16, 0], "texture": "#0"},
|
||||
"down": {"uv": [13, 0, 16, 6], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -89,12 +65,12 @@
|
||||
"to": [10.5, 15.5, 10.5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, -0.5, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 10, 2.5, 12.5], "texture": "#0"},
|
||||
"east": {"uv": [0, 10, 2.5, 12.5], "texture": "#0"},
|
||||
"south": {"uv": [0, 10, 2.5, 12.5], "texture": "#0"},
|
||||
"west": {"uv": [2.5, 10, 0, 12.5], "texture": "#0"},
|
||||
"up": {"uv": [0, 12.5, 2.5, 10], "texture": "#0"},
|
||||
"down": {"uv": [0, 10, 2.5, 12.5], "texture": "#0"}
|
||||
"north": {"uv": [13.5, 6, 16, 11], "texture": "#0"},
|
||||
"east": {"uv": [13.5, 6, 16, 11], "texture": "#0"},
|
||||
"south": {"uv": [13.5, 6, 16, 11], "texture": "#0"},
|
||||
"west": {"uv": [16, 6, 13.5, 11], "texture": "#0"},
|
||||
"up": {"uv": [13.5, 11, 16, 6], "texture": "#0"},
|
||||
"down": {"uv": [13.5, 6, 16, 11], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -102,12 +78,8 @@
|
||||
"to": [11, 10, 11],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"south": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"west": {"uv": [3, 13, 0, 16], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 3, 13], "texture": "#0"},
|
||||
"down": {"uv": [0, 16, 3, 13], "texture": "#0"}
|
||||
"up": {"uv": [13, 6, 16, 0], "texture": "#0"},
|
||||
"down": {"uv": [13, 6, 16, 0], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -115,12 +87,8 @@
|
||||
"to": [5, 16, 11],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"south": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"west": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 3, 13], "texture": "#0"},
|
||||
"down": {"uv": [0, 13, 3, 16], "texture": "#0"}
|
||||
"east": {"uv": [13, 0, 16, 6], "texture": "#0"},
|
||||
"west": {"uv": [13, 0, 16, 6], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -128,12 +96,8 @@
|
||||
"to": [11, 16, 11],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"south": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"west": {"uv": [3, 13, 0, 16], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 3, 13], "texture": "#0"},
|
||||
"down": {"uv": [0, 13, 3, 16], "texture": "#0"}
|
||||
"north": {"uv": [13, 0, 16, 6], "texture": "#0"},
|
||||
"south": {"uv": [13, 0, 16, 6], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -141,12 +105,8 @@
|
||||
"to": [11, 16, 5],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"south": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"west": {"uv": [3, 13, 0, 16], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 3, 13], "texture": "#0"},
|
||||
"down": {"uv": [0, 13, 3, 16], "texture": "#0"}
|
||||
"north": {"uv": [13, 0, 16, 6], "texture": "#0"},
|
||||
"south": {"uv": [13, 0, 16, 6], "texture": "#0"}
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -154,12 +114,8 @@
|
||||
"to": [11, 16, 11],
|
||||
"rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]},
|
||||
"faces": {
|
||||
"north": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"east": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"south": {"uv": [0, 13, 3, 16], "texture": "#0"},
|
||||
"west": {"uv": [3, 13, 0, 16], "texture": "#0"},
|
||||
"up": {"uv": [0, 16, 3, 13], "texture": "#0"},
|
||||
"down": {"uv": [0, 13, 3, 16], "texture": "#0"}
|
||||
"east": {"uv": [13, 0, 16, 6], "texture": "#0"},
|
||||
"west": {"uv": [16, 0, 13, 6], "texture": "#0"}
|
||||
}
|
||||
}
|
||||
],
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_0",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_0"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_1",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_1"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_1"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_2",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_2"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_2"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_3",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_3"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_3"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_4",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_4"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_4"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_5",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_5"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_5"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_6",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_6"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_6"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_7",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_7"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_7"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_8",
|
||||
"particle": "cobblemon:block/crops/revival_herb_stage_8"
|
||||
"0": "cobblemon:block/crops/revival_herb_stage_8"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/white_herb_stage_0",
|
||||
"particle": "cobblemon:block/crops/white_herb_stage_0"
|
||||
"0": "cobblemon:block/crops/white_herb_stage_0"
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
{
|
||||
"parent": "cobblemon:block/revival_herb",
|
||||
"textures": {
|
||||
"0": "cobblemon:block/crops/white_herb_stage_1",
|
||||
"particle": "cobblemon:block/crops/white_herb_stage_1"
|
||||
"0": "cobblemon:block/crops/white_herb_stage_1"
|
||||
}
|
||||
}
|
||||
|
Before Width: | Height: | Size: 239 B After Width: | Height: | Size: 469 B |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 460 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 539 B |
|
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 578 B |
|
Before Width: | Height: | Size: 341 B After Width: | Height: | Size: 552 B |
|
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 593 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 539 B |
|
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 578 B |
|
Before Width: | Height: | Size: 225 B After Width: | Height: | Size: 406 B |
|
Before Width: | Height: | Size: 242 B After Width: | Height: | Size: 444 B |
|
Before Width: | Height: | Size: 247 B After Width: | Height: | Size: 461 B |
|
Before Width: | Height: | Size: 259 B After Width: | Height: | Size: 467 B |
|
Before Width: | Height: | Size: 275 B After Width: | Height: | Size: 483 B |
|
Before Width: | Height: | Size: 296 B After Width: | Height: | Size: 496 B |
|
Before Width: | Height: | Size: 302 B After Width: | Height: | Size: 508 B |
|
Before Width: | Height: | Size: 340 B After Width: | Height: | Size: 544 B |
|
Before Width: | Height: | Size: 402 B After Width: | Height: | Size: 585 B |
|
Before Width: | Height: | Size: 333 B After Width: | Height: | Size: 539 B |
|
Before Width: | Height: | Size: 397 B After Width: | Height: | Size: 578 B |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 298 B After Width: | Height: | Size: 528 B |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 525 B |
|
Before Width: | Height: | Size: 449 B After Width: | Height: | Size: 543 B |
|
Before Width: | Height: | Size: 404 B After Width: | Height: | Size: 572 B |
|
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 605 B |
|
Before Width: | Height: | Size: 435 B After Width: | Height: | Size: 603 B |
|
Before Width: | Height: | Size: 437 B After Width: | Height: | Size: 538 B |
|
Before Width: | Height: | Size: 2.9 KiB After Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 304 B |
|
Before Width: | Height: | Size: 288 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |
|
Before Width: | Height: | Size: 283 B After Width: | Height: | Size: 503 B |