mirror of
https://gitlab.com/cable-mc/cobblemon.git
synced 2026-10-20 22:26:24 -04:00
Compare commits
11
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d85fb7e160 | ||
|
|
c56f8b098f | ||
|
|
e7197a7d3a | ||
|
|
d2febef6cf | ||
|
|
d98145617f | ||
|
|
c7b7fd3236 | ||
|
|
0a18c67e34 | ||
|
|
6341ca417f | ||
|
|
fe6841cac4 | ||
|
|
0536c98b6a | ||
|
|
fc73384ea2 |
@@ -9,6 +9,7 @@
|
||||
package com.cobblemon.mod.common
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonAttackTargetData
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonAvoidTargetData
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonWanderControl
|
||||
import com.cobblemon.mod.common.api.dialogue.ActiveDialogue
|
||||
import com.cobblemon.mod.common.api.moves.animations.ActionEffectContext
|
||||
@@ -57,9 +58,11 @@ object CobblemonMemories {
|
||||
/** how many are following me rn? */
|
||||
val HERD_SIZE = register<Int>("herd_size") // Don't bother saving it, we'll try to keep count roughly.
|
||||
val ATTACK_TARGET_DATA = register<CobblemonAttackTargetData>("attack_target_data") // This is used to store additional information about the attack target, such as when to give up pursuit.
|
||||
val AVOID_TARGET_DATA = register<CobblemonAvoidTargetData>("avoid_target_data") // This is used to store additional information about the avoid target, such as how fast to move.
|
||||
val WANDER_CONTROL = register<CobblemonWanderControl>("wander_control")
|
||||
val DISABLE_WALK_TO_WANTED_ITEM = register<Boolean>("disable_walk_to_wanted_item")
|
||||
val TIME_TRYING_TO_REACH_WANTED_ITEM = register<Int>("time_trying_to_wanted_item")
|
||||
val RECENT_CATCH_ATTEMPT = register<UUID>("recent_catch_attempt")
|
||||
|
||||
fun <U> register(id: String, codec: Codec<U>): MemoryModuleType<U> {
|
||||
val memoryModule = MemoryModuleType(Optional.of(codec))
|
||||
|
||||
+22
-7
@@ -20,6 +20,8 @@ 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
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
|
||||
/**
|
||||
* This is used in the AI system to store optional additional information about the attack target,
|
||||
@@ -41,15 +43,21 @@ import net.minecraft.world.entity.Entity
|
||||
class CobblemonAttackTargetData(
|
||||
/** An expression that returns false when the target is no longer worth attacking. */
|
||||
var shouldContinue: Expression = "true".asExpression(),
|
||||
var hitsUntilDisengage: Int = -1, // -1 means no limit
|
||||
var disengageAction: (entity: Entity) -> Unit = {}
|
||||
var dealtHitsUntilDisengage: Int = -1, // -1 means no limit
|
||||
var disengageAction: (entity: LivingEntity) -> Unit = ::defaultDisengage
|
||||
) {
|
||||
companion object {
|
||||
fun defaultDisengage(entity: LivingEntity) {
|
||||
entity.brain.eraseMemory(MemoryModuleType.ATTACK_TARGET)
|
||||
entity.brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime)
|
||||
}
|
||||
}
|
||||
val struct = ObjectValue<CobblemonAttackTargetData>(this).also {
|
||||
it.addFunction("hits_until_disengage") { DoubleValue(hitsUntilDisengage.toDouble()) }
|
||||
it.addFunction("set_hits_until_disengage") { params ->
|
||||
it.addFunction("dealt_hits_until_disengage") { DoubleValue(dealtHitsUntilDisengage.toDouble()) }
|
||||
it.addFunction("set_dealt_hits_until_disengage") { params ->
|
||||
val value = params.getDouble(0)
|
||||
hitsUntilDisengage = value.toInt()
|
||||
DoubleValue(hitsUntilDisengage.toDouble())
|
||||
dealtHitsUntilDisengage = value.toInt()
|
||||
DoubleValue(dealtHitsUntilDisengage.toDouble())
|
||||
}
|
||||
it.addFunction("set_disengage_action") { params ->
|
||||
val script = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
@@ -64,8 +72,15 @@ class CobblemonAttackTargetData(
|
||||
}
|
||||
DoubleValue.ONE
|
||||
}
|
||||
it.addFunction("reset") {
|
||||
shouldContinue = "true".asExpression()
|
||||
dealtHitsUntilDisengage = -1
|
||||
disengageAction = ::defaultDisengage
|
||||
DoubleValue.ONE
|
||||
}
|
||||
it.addFunction("unset_disengage_action") { params ->
|
||||
disengageAction = {}
|
||||
disengageAction = ::defaultDisengage
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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 com.bedrockk.molang.runtime.value.DoubleValue
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
|
||||
class CobblemonAvoidTargetData {
|
||||
var walkSpeed: Float? = null
|
||||
|
||||
@Transient
|
||||
val struct = ObjectValue<CobblemonAvoidTargetData>(this).also {
|
||||
it.addFunction("walk_speed") { DoubleValue(walkSpeed) }
|
||||
it.addFunction("set_walk_speed") { walkSpeed = it.getDoubleOrNull(0)?.toFloat() }
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import com.cobblemon.mod.common.util.getBooleanOrNull
|
||||
import com.cobblemon.mod.common.util.getDoubleOrNull
|
||||
import kotlin.math.sqrt
|
||||
import net.minecraft.core.BlockPos
|
||||
import net.minecraft.world.entity.PathfinderMob
|
||||
|
||||
/**
|
||||
* Used to maintain some settings around how the entity should wander this tick. The idea is you
|
||||
@@ -30,7 +29,8 @@ class CobblemonWanderControl(
|
||||
var allowWater: Boolean = false,
|
||||
var allowAir: Boolean = false,
|
||||
var pathCooldownTicks: Int = 20 * 5, // Used to mitigate the cost of pathfinding.
|
||||
var wanderSpeed: Float = 0.35F,
|
||||
var speedMultiplier: Float = 1F,
|
||||
var wanderChance: Float? = null
|
||||
) {
|
||||
class WanderCenter(val x: Number, val y: Number, val z: Number, val minRange: Float, val maxRange: Float)
|
||||
|
||||
@@ -46,7 +46,7 @@ class CobblemonWanderControl(
|
||||
.addFunction("allow_water") { if (allowWater) 1.0 else 0.0 }
|
||||
.addFunction("allow_air") { if (allowAir) 1.0 else 0.0 }
|
||||
.addFunction("path_cooldown_ticks") { pathCooldownTicks.toDouble() }
|
||||
.addFunction("wander_speed") { wanderSpeed }
|
||||
.addFunction("speed_multiplier") { speedMultiplier }
|
||||
.addFunction("set_center") { params ->
|
||||
if (params.params.size < 3) {
|
||||
throw IllegalArgumentException("set_center requires at least 3 parameters: x, y, z")
|
||||
@@ -69,15 +69,19 @@ class CobblemonWanderControl(
|
||||
.addFunction("set_allow_water") { params -> allowWater = params.getBooleanOrNull(0) ?: false }
|
||||
.addFunction("set_allow_air") { params -> allowAir = params.getBooleanOrNull(0) ?: false }
|
||||
.addFunction("set_path_cooldown_ticks") { params -> pathCooldownTicks = params.getDoubleOrNull(0)?.toInt() ?: (20 * 5) }
|
||||
.addFunction("set_wander_speed") { params -> wanderSpeed = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
|
||||
.addFunction("set_speed_multiplier") { params -> speedMultiplier = params.getDoubleOrNull(0)?.toFloat() ?: 0.35F }
|
||||
.addFunction("set_wander_chance") { params ->
|
||||
wanderChance = params.getDoubleOrNull(0)?.toFloat()
|
||||
}
|
||||
.addFunction("reset") {
|
||||
center = null
|
||||
allowLand = false
|
||||
allowWater = false
|
||||
allowAir = false
|
||||
pathCooldownTicks = 20 * 5
|
||||
wanderSpeed = 0.35F
|
||||
speedMultiplier = 1F
|
||||
maxAttempts = 4
|
||||
wanderChance = null
|
||||
}
|
||||
|
||||
fun isSuitable(position: BlockPos): Boolean {
|
||||
|
||||
+87
@@ -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.api.ai.config.task
|
||||
|
||||
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
|
||||
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
|
||||
import com.cobblemon.mod.common.api.ai.asVariables
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.entity.EntityCallbacks
|
||||
import com.cobblemon.mod.common.entity.MoLangScriptingEntity
|
||||
import com.cobblemon.mod.common.util.asExpression
|
||||
import com.mojang.datafixers.util.Either
|
||||
import net.minecraft.server.level.ServerLevel
|
||||
import net.minecraft.world.entity.LivingEntity
|
||||
import net.minecraft.world.entity.ai.behavior.Behavior
|
||||
import net.minecraft.world.entity.ai.behavior.BehaviorControl
|
||||
import net.minecraft.world.entity.ai.memory.MemoryModuleType
|
||||
import net.minecraft.world.entity.ai.memory.MemoryStatus
|
||||
import net.minecraft.world.entity.ai.sensing.SensorType
|
||||
|
||||
/**
|
||||
* Frequently checks a diff on the visibility of entities around the given entity so EntityCallbacks can be triggered.
|
||||
*
|
||||
* @author Hiroku
|
||||
* @since August 7th, 2025
|
||||
*/
|
||||
class NotifyAboutEntityVisibilityTaskConfig : SingleTaskConfig {
|
||||
val ticksBetweenChecks: ExpressionOrEntityVariable = Either.left("20".asExpression())
|
||||
|
||||
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(ticksBetweenChecks).asVariables()
|
||||
override fun createTask(
|
||||
entity: LivingEntity,
|
||||
behaviourConfigurationContext: BehaviourConfigurationContext
|
||||
): BehaviorControl<in LivingEntity>? {
|
||||
if (entity !is MoLangScriptingEntity) {
|
||||
return null
|
||||
}
|
||||
|
||||
val ticksBetweenChecks = ticksBetweenChecks.resolveInt(behaviourConfigurationContext.runtime)
|
||||
|
||||
behaviourConfigurationContext.addMemories(MemoryModuleType.NEAREST_LIVING_ENTITIES)
|
||||
behaviourConfigurationContext.addSensors(SensorType.NEAREST_LIVING_ENTITIES)
|
||||
|
||||
return object : Behavior<LivingEntity>(mapOf(MemoryModuleType.NEAREST_LIVING_ENTITIES to MemoryStatus.VALUE_PRESENT), Int.MAX_VALUE) {
|
||||
override fun canStillUse(level: ServerLevel, entity: LivingEntity, gameTime: Long) = true
|
||||
override fun checkExtraStartConditions(level: ServerLevel, owner: LivingEntity) = true
|
||||
|
||||
// Buffer of known entities
|
||||
val knownEntities = mutableListOf<LivingEntity>()
|
||||
override fun tick(level: ServerLevel, owner: LivingEntity, gameTime: Long) {
|
||||
if (gameTime % ticksBetweenChecks != 0L) {
|
||||
return
|
||||
}
|
||||
|
||||
val visibleEntities = owner.getBrain().getMemory(MemoryModuleType.NEAREST_LIVING_ENTITIES)
|
||||
.orElse(emptyList<LivingEntity>())
|
||||
.filter { it.isAlive }
|
||||
|
||||
val newEntities = visibleEntities.filterNot { it in knownEntities }
|
||||
val removedEntities = knownEntities.filterNot { it in visibleEntities }
|
||||
|
||||
newEntities.forEach {
|
||||
entity.callbacks.process(
|
||||
type = EntityCallbacks.SAW_ENTITY,
|
||||
functions = mapOf("other_entity" to { _ -> it.asMostSpecificMoLangValue() })
|
||||
)
|
||||
}
|
||||
|
||||
removedEntities.forEach {
|
||||
entity.callbacks.process(
|
||||
type = EntityCallbacks.LOST_ENTITY,
|
||||
functions = mapOf("other_entity" to { _ -> it.asMostSpecificMoLangValue() })
|
||||
)
|
||||
}
|
||||
|
||||
knownEntities.clear()
|
||||
knownEntities.addAll(visibleEntities)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -79,7 +79,6 @@ interface TaskConfig {
|
||||
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
|
||||
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
|
||||
cobblemonResource("walk_away_from_avoid_target") to WalkAwayFromAvoidTargetTaskConfig::class.java,
|
||||
cobblemonResource("flee_nearest_hostile") to FleeNearestHostileTaskConfig::class.java,
|
||||
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::class.java,
|
||||
cobblemonResource("fly_in_circles") to FlyInCirclesTaskConfig::class.java,
|
||||
cobblemonResource("run_script") to RunScript::class.java,
|
||||
@@ -120,6 +119,7 @@ interface TaskConfig {
|
||||
cobblemonResource("target_entity") to TargetEntityTaskConfig::class.java,
|
||||
cobblemonResource("memory_aspect") to MemoryAspectTaskConfig::class.java,
|
||||
cobblemonResource("activity_change") to ActivityChangeTaskConfig::class.java,
|
||||
cobblemonResource("notify_about_entity_visibility") to NotifyAboutEntityVisibilityTaskConfig::class.java,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -133,7 +133,7 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
val wanderControl = it.tryGet(wanderControl).orElse(null) ?: CobblemonWanderControl()
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val avoidsTargetingAir = avoidTargetingAir.resolveBoolean(mainThreadRuntime)
|
||||
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
|
||||
val wanderChance = wanderControl.wanderChance ?: mainThreadRuntime.resolveFloat(wanderChanceExpression)
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance || !wanderControl.allowLand) {
|
||||
return@Trigger false
|
||||
}
|
||||
@@ -180,7 +180,7 @@ class WanderTaskConfig : SingleTaskConfig {
|
||||
walkTarget.set(
|
||||
CobblemonWalkTarget(
|
||||
pos = pos,
|
||||
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
|
||||
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime) * wanderControl.speedMultiplier,
|
||||
completionRange = 0,
|
||||
nodeTypeFilter = { nodeType -> nodeType !in listOf(PathType.WATER, PathType.WATER_BORDER) },
|
||||
destinationNodeTypeFilter = { nodeType -> !avoidsTargetingAir || nodeType !in listOf(PathType.OPEN) }
|
||||
|
||||
+2
-2
@@ -78,7 +78,7 @@ class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
}
|
||||
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val wanderChance = mainThreadRuntime.resolveFloat(wanderChanceExpression)
|
||||
val wanderChance = wanderControl.wanderChance ?: mainThreadRuntime.resolveFloat(wanderChanceExpression)
|
||||
if (wanderChance <= 0 || world.random.nextFloat() > wanderChance) {
|
||||
return@Trigger false
|
||||
}
|
||||
@@ -104,7 +104,7 @@ class WaterWanderTaskConfig : SingleTaskConfig {
|
||||
CobblemonWalkTarget(
|
||||
pos = pos,
|
||||
nodeTypeFilter = { it == PathType.WATER || it == PathType.WATER_BORDER },
|
||||
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime),
|
||||
speedModifier = speedMultiplier.resolveFloat(mainThreadRuntime) * wanderControl.speedMultiplier,
|
||||
completionRange = 0
|
||||
)
|
||||
)
|
||||
|
||||
@@ -12,7 +12,6 @@ import com.bedrockk.molang.runtime.MoLangEnvironment
|
||||
import com.bedrockk.molang.runtime.MoLangRuntime
|
||||
import com.bedrockk.molang.runtime.MoParams
|
||||
import com.bedrockk.molang.runtime.struct.ArrayStruct
|
||||
import com.bedrockk.molang.runtime.struct.MoStruct
|
||||
import com.bedrockk.molang.runtime.struct.QueryStruct
|
||||
import com.bedrockk.molang.runtime.struct.VariableStruct
|
||||
import com.bedrockk.molang.runtime.value.DoubleValue
|
||||
@@ -25,6 +24,8 @@ 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.CobblemonAttackTargetData
|
||||
import com.cobblemon.mod.common.api.ai.CobblemonAvoidTargetData
|
||||
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
|
||||
@@ -348,10 +349,7 @@ object MoLangFunctions {
|
||||
val b = format.parse(dateB)
|
||||
DoubleValue(a.after(b))
|
||||
},
|
||||
"create_simple_party_provider" to java.util.function.Function { params ->
|
||||
val partyProvider = SimplePartyProvider()
|
||||
return@Function partyProvider.struct
|
||||
},
|
||||
"create_simple_party_provider" to java.util.function.Function { SimplePartyProvider().struct },
|
||||
"create_pickup_item" to java.util.function.Function { params ->
|
||||
val item = params.getOrNull<MoValue>(0)?.asString()?.let(ObtainableItemCondition::parseFromString)
|
||||
val pickupPriority = params.getIntOrNull(1) ?: 0
|
||||
@@ -364,6 +362,7 @@ object MoLangFunctions {
|
||||
{ worldHolder ->
|
||||
val world = worldHolder.value()
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("id") { StringValue(world.dimension().location().toString()) }
|
||||
map.put("is_time_of_day") { params ->
|
||||
val timeOfDay = TimeRange.timeRanges[params.getString(0).lowercase()]
|
||||
?: return@put DoubleValue.ZERO
|
||||
@@ -807,6 +806,13 @@ object MoLangFunctions {
|
||||
val source = DamageSource(entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE).getHolder(DamageTypes.GENERIC).get())
|
||||
entity.hurt(source, amount.toFloat())
|
||||
}
|
||||
map.put("damage_source") { params ->
|
||||
val registry = entity.level().registryAccess().registryOrThrow(Registries.DAMAGE_TYPE)
|
||||
val type = params.getStringOrNull(0)?.asIdentifierDefaultingNamespace()?.let(registry::getHolder)
|
||||
?: registry.getHolder(DamageTypes.GENERIC)
|
||||
val damageSource = DamageSource(type.get(), entity)
|
||||
return@put damageSource.asMoLangValue()
|
||||
}
|
||||
map.put("distance_to_owner") {
|
||||
if (entity !is TamableAnimal) {
|
||||
return@put DoubleValue.ZERO
|
||||
@@ -930,6 +936,10 @@ object MoLangFunctions {
|
||||
map.put("is_on_fire") { _ -> DoubleValue(entity.isOnFire) }
|
||||
map.put("is_invisible") { _ -> DoubleValue(entity.isInvisible) }
|
||||
map.put("is_riding") { _ -> DoubleValue(entity.isPassenger) }
|
||||
map.put("distance_to_entity") { params ->
|
||||
val otherEntity = params.get<MoValue>(0) as? ObjectValue<Entity> ?: return@put DoubleValue.ZERO
|
||||
return@put DoubleValue(entity.distanceTo(otherEntity.obj))
|
||||
}
|
||||
map.put("distance_to_pos") { params ->
|
||||
val x = params.getDouble(0)
|
||||
val y = params.getDouble(1)
|
||||
@@ -1063,7 +1073,19 @@ object MoLangFunctions {
|
||||
}
|
||||
)
|
||||
|
||||
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>>(
|
||||
val damageSourceFunctions: MutableList<(DamageSource) -> java.util.HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ source ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("direct_entity") { source.directEntity?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO }
|
||||
map.put("entity") { source.entity?.asMostSpecificMoLangValue() ?: DoubleValue.ZERO }
|
||||
map.put("is_food_exhaustion") { DoubleValue(source.foodExhaustion) }
|
||||
map.put("is_direct") { DoubleValue(source.isDirect) }
|
||||
map.put("type") { StringValue(source.typeHolder().unwrapKey().get().location().toString()) }
|
||||
return@mutableListOf map
|
||||
}
|
||||
)
|
||||
|
||||
val livingEntityFunctions: MutableList<(LivingEntity) -> HashMap<String, java.util.function.Function<MoParams, Any>>> = mutableListOf(
|
||||
{ entity ->
|
||||
val map = hashMapOf<String, java.util.function.Function<MoParams, Any>>()
|
||||
map.put("is_player") { _ -> DoubleValue(entity is Player) }
|
||||
@@ -1105,6 +1127,23 @@ object MoLangFunctions {
|
||||
val amount = params.getDouble(0)
|
||||
entity.heal(amount.toFloat())
|
||||
}
|
||||
map.put("reset_activity") { _ ->
|
||||
entity.brain.updateActivityFromSchedule(entity.level().dayTime, entity.level().gameTime)
|
||||
DoubleValue.ONE
|
||||
}
|
||||
map.put("activity") { _ ->
|
||||
return@put entity.brain.activeNonCoreActivity.map { StringValue(it.name) }.orElse(null) ?: DoubleValue.ZERO
|
||||
}
|
||||
map.put("set_activity") { params ->
|
||||
val activityId = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val activity = BuiltInRegistries.ACTIVITY.get(activityId)
|
||||
if (activity != null) {
|
||||
entity.brain.setActiveActivityIfPossible(activity)
|
||||
return@put DoubleValue.ONE
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("is_looking_at") { params ->
|
||||
val targetEntity = params.get<MoValue>(0)
|
||||
val maxDistance = params.getDoubleOrNull(1)?.toFloat() ?: 2.0F
|
||||
@@ -1131,6 +1170,26 @@ object MoLangFunctions {
|
||||
val brain = entity.brain
|
||||
brain.setMemoryWithExpiry(MemoryModuleType.LOOK_TARGET, CobblemonBlockPosTracker(Vec3(x, y, z), flags.toSet()), duration.toLong())
|
||||
}
|
||||
map.put("is_doing_activity") { params ->
|
||||
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
|
||||
val activities = identifiers.mapNotNull(BuiltInRegistries.ACTIVITY::get)
|
||||
if (activities.isNotEmpty()) {
|
||||
return@put DoubleValue(activities.any(entity.brain::isActive))
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("get_attack_target_data_memory") {
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.ATTACK_TARGET_DATA).orElse(null)
|
||||
?: CobblemonAttackTargetData()
|
||||
if (
|
||||
!entity.brain.checkMemory(CobblemonMemories.ATTACK_TARGET_DATA, MemoryStatus.VALUE_PRESENT)
|
||||
&& entity.brain.checkMemory(CobblemonMemories.ATTACK_TARGET_DATA, MemoryStatus.REGISTERED)
|
||||
) {
|
||||
entity.brain.setMemory(CobblemonMemories.ATTACK_TARGET_DATA, value)
|
||||
}
|
||||
return@put value.struct
|
||||
}
|
||||
map.put("get_wander_control_memory") {
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.WANDER_CONTROL).orElse(null)
|
||||
?: CobblemonWanderControl()
|
||||
@@ -1142,6 +1201,17 @@ object MoLangFunctions {
|
||||
}
|
||||
return@put value.struct
|
||||
}
|
||||
map.put("get_avoid_target_data_memory") {
|
||||
val value = entity.brain.getMemorySafely(CobblemonMemories.AVOID_TARGET_DATA).orElse(null)
|
||||
?: CobblemonAvoidTargetData()
|
||||
if (
|
||||
!entity.brain.checkMemory(CobblemonMemories.AVOID_TARGET_DATA, MemoryStatus.VALUE_PRESENT)
|
||||
&& entity.brain.checkMemory(CobblemonMemories.AVOID_TARGET_DATA, MemoryStatus.REGISTERED)
|
||||
) {
|
||||
entity.brain.setMemory(CobblemonMemories.AVOID_TARGET_DATA, value)
|
||||
}
|
||||
return@put value.struct
|
||||
}
|
||||
map.put("get_position_memory") { params ->
|
||||
val id = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
|
||||
@@ -1168,6 +1238,30 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("get_entity_memory") { params ->
|
||||
val id = params.getString(0).asIdentifierDefaultingNamespace()
|
||||
val memoryType = BuiltInRegistries.MEMORY_MODULE_TYPE.get(id)
|
||||
if (entity.brain.checkMemory(memoryType, MemoryStatus.VALUE_PRESENT)) {
|
||||
val memory = entity.brain.getMemory(memoryType).get()
|
||||
return@put when (memory) {
|
||||
is Entity -> memory.asMostSpecificMoLangValue()
|
||||
else -> DoubleValue.ZERO
|
||||
}
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("set_entity_memory") { params ->
|
||||
val memory = params.getString(0).asIdentifierDefaultingNamespace().let(BuiltInRegistries.MEMORY_MODULE_TYPE::get) as MemoryModuleType<LivingEntity>
|
||||
val entityValue = (params.get<MoValue>(1) as? ObjectValue<*>)?.obj as? LivingEntity ?: return@put DoubleValue.ZERO
|
||||
val expiry = params.getIntOrNull(2) ?: -1
|
||||
if (expiry != -1) {
|
||||
entity.brain.setMemoryWithExpiry(memory, entityValue, expiry.toLong())
|
||||
} else {
|
||||
entity.brain.setMemory(memory, entityValue)
|
||||
}
|
||||
return@put DoubleValue.ONE
|
||||
}
|
||||
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
|
||||
@@ -1214,6 +1308,18 @@ object MoLangFunctions {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("is_visible_entity") { params ->
|
||||
val otherEntity = params.get<ObjectValue<*>>(0)?.obj as? LivingEntity ?: return@put DoubleValue.ZERO
|
||||
val visibleMobs = entity.brain.getMemorySafely(MemoryModuleType.NEAREST_VISIBLE_LIVING_ENTITIES).orElse(null)
|
||||
?: return@put DoubleValue.ZERO
|
||||
return@put DoubleValue(visibleMobs.contains(otherEntity))
|
||||
}
|
||||
map.put("is_nearby_entity") { params ->
|
||||
val otherEntity = params.get<ObjectValue<*>>(0)?.obj as? LivingEntity ?: return@put DoubleValue.ZERO
|
||||
val nearbyMobs = entity.brain.getMemorySafely(MemoryModuleType.NEAREST_LIVING_ENTITIES).orElse(null)
|
||||
?: return@put DoubleValue.ZERO
|
||||
return@put DoubleValue(nearbyMobs.contains(otherEntity))
|
||||
}
|
||||
if (entity is PathfinderMob) {
|
||||
map.put("walk_to") { params ->
|
||||
val x = params.getDouble(0)
|
||||
@@ -1252,15 +1358,6 @@ object MoLangFunctions {
|
||||
map.put("in_battle") { DoubleValue(npc.isInBattle()) }
|
||||
map.put("battles") { ArrayStruct(npc.battleIds.mapNotNull { BattleRegistry.getBattle(it)?.struct }.mapIndexed { index, value -> "$index" to value }.toMap()) }
|
||||
map.put("stop_battles") { _ -> npc.battleIds.forEach { BattleRegistry.getBattle(it)?.stop() } }
|
||||
map.put("is_doing_activity") { params ->
|
||||
val identifiers = params.params.map { it.asString().asIdentifierDefaultingNamespace(namespace = "minecraft") }
|
||||
val activities = identifiers.mapNotNull { identifier -> BuiltInRegistries.ACTIVITY.get(identifier) }
|
||||
if (activities.isNotEmpty()) {
|
||||
return@put DoubleValue(activities.any { activity -> npc.brain.isActive(activity) })
|
||||
} else {
|
||||
return@put DoubleValue.ZERO
|
||||
}
|
||||
}
|
||||
map.put("run_script_on_client") { params ->
|
||||
val world = npc.level()
|
||||
if (world is ServerLevel) {
|
||||
@@ -2471,6 +2568,15 @@ object MoLangFunctions {
|
||||
return value
|
||||
}
|
||||
|
||||
fun DamageSource.asMoLangValue(): ObjectValue<DamageSource> {
|
||||
val value = ObjectValue(
|
||||
obj = this,
|
||||
stringify = { it.toString() }
|
||||
)
|
||||
value.addFunctions(damageSourceFunctions.flatMap { it(this).entries.map { it.key to it.value } }.toMap())
|
||||
return value
|
||||
}
|
||||
|
||||
/**
|
||||
* Different functions exist depending on the entity type, this tries to make the struct that's most specific to the type.
|
||||
*/
|
||||
|
||||
@@ -160,7 +160,7 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
createDefault("great_ball", MultiplierModifier(1.5F))
|
||||
createDefault("ultra_ball", MultiplierModifier(2F))
|
||||
createDefault("master_ball", GuaranteedModifier())
|
||||
createDefault("safari_ball", CatchRateModifiers.SAFARI)
|
||||
createDefault("safari_ball", CatchRateModifiers.SAFARI, stealthFactor = 0.75f)
|
||||
createDefault("fast_ball", BaseStatModifier(Stats.SPEED, { it >= 100 }, 4F))
|
||||
createDefault("level_ball", CatchRateModifiers.LEVEL)
|
||||
createDefault("lure_ball", CatchRateModifiers.LURE)
|
||||
@@ -237,11 +237,12 @@ object PokeBalls : JsonDataRegistry<PokeBall> {
|
||||
model2d: ResourceLocation = cobblemonResource(name),
|
||||
model3d: ResourceLocation = cobblemonResource("item/${name}_model"),
|
||||
throwPower: Float = 1.25f,
|
||||
ancient: Boolean = false
|
||||
ancient: Boolean = false,
|
||||
stealthFactor: Float = 0.0f,
|
||||
): PokeBall {
|
||||
val identifier = cobblemonResource(name)
|
||||
//val finalModifiers = if (appendUltraBeastPenalty) modifiers + listOf(LabelModifier(0.1F, true, CobblemonPokemonLabels.ULTRA_BEAST)) else modifiers
|
||||
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient)
|
||||
val pokeball = PokeBall(identifier, modifier, effects, waterDragValue, model2d, model3d, throwPower, ancient, stealthFactor)
|
||||
this.defaults[identifier] = pokeball
|
||||
return pokeball
|
||||
}
|
||||
|
||||
@@ -29,9 +29,11 @@ object BattleRegistry {
|
||||
.registerTypeAdapter(ShowdownMoveset::class.java, ShowdownMovesetAdapter)
|
||||
.create()
|
||||
private val battleMap = ConcurrentHashMap<UUID, PokemonBattle>()
|
||||
private val battleLock = ConcurrentHashMap<UUID, Boolean>()
|
||||
|
||||
fun onServerStarted() {
|
||||
battleMap.clear()
|
||||
battleLock.clear()
|
||||
}
|
||||
|
||||
fun onPlayerDisconnect(player: ServerPlayer) {
|
||||
@@ -205,12 +207,18 @@ object BattleRegistry {
|
||||
startShowdown(battle)
|
||||
}
|
||||
|
||||
if (!canPreempt) start().also { return SuccessfulBattleStart(battle) }
|
||||
if (!canPreempt) start().also {
|
||||
side1.actors.forEach { actor -> battleLock.remove(actor.uuid) }
|
||||
side2.actors.forEach { actor -> battleLock.remove(actor.uuid) }
|
||||
return SuccessfulBattleStart(battle)
|
||||
}
|
||||
|
||||
val preBattleEvent = BattleStartedEvent.Pre(battle)
|
||||
CobblemonEvents.BATTLE_STARTED_PRE.postThen(preBattleEvent) {
|
||||
start()
|
||||
CobblemonEvents.BATTLE_STARTED_POST.post(BattleStartedEvent.Post(battle))
|
||||
side1.actors.forEach { actor -> battleLock.remove(actor.uuid) }
|
||||
side2.actors.forEach { actor -> battleLock.remove(actor.uuid) }
|
||||
return SuccessfulBattleStart(battle)
|
||||
}
|
||||
return ErroredBattleStart(mutableSetOf(BattleStartError.canceledByEvent(preBattleEvent.reason)))
|
||||
@@ -229,6 +237,19 @@ object BattleRegistry {
|
||||
return battleMap.values.find { it.getActor(serverPlayer) != null }
|
||||
}
|
||||
|
||||
fun checkBattleLock(serverPlayer: ServerPlayer, ancientIsOkay: Boolean): Boolean =
|
||||
if (ancientIsOkay)
|
||||
battleLock.get(serverPlayer.uuid) == false
|
||||
else
|
||||
battleLock.containsKey(serverPlayer.uuid)
|
||||
|
||||
fun lockPlayer(serverPlayer: ServerPlayer, ancient: Boolean) {
|
||||
battleLock.put(serverPlayer.uuid, ancient)
|
||||
}
|
||||
fun unlockPlayer(serverPlayer: ServerPlayer) {
|
||||
battleLock.remove(serverPlayer.uuid)
|
||||
}
|
||||
|
||||
fun getBattleByParticipatingPlayerId(playerId: UUID): PokemonBattle? {
|
||||
return battleMap.values.find { playerId in it.playerUUIDs }
|
||||
}
|
||||
|
||||
@@ -249,7 +249,7 @@ class ForcePassActionResponse : ShowdownActionResponse(ShowdownActionResponseTyp
|
||||
return false
|
||||
}
|
||||
|
||||
return activeBattlePokemon.actor.expectingPassActions.size > 0
|
||||
return activeBattlePokemon.actor.expectingPassActions.isNotEmpty()
|
||||
}
|
||||
|
||||
override fun toShowdownString(activeBattlePokemon: ActiveBattlePokemon, showdownMoveSet: ShowdownMoveset?) = "pass"
|
||||
|
||||
@@ -38,6 +38,7 @@ class EntityCallbacks(val entity: Entity) : HashMap<ResourceLocation, MutableLis
|
||||
companion object {
|
||||
val HIT_BY_POKEBALL = cobblemonResource("hit_by_pokeball")
|
||||
val SAW_ENTITY = cobblemonResource("saw_entity")
|
||||
val LOST_ENTITY = cobblemonResource("lost_sight_of_entity")
|
||||
val HURT = cobblemonResource("hurt")
|
||||
val DIED = cobblemonResource("died")
|
||||
}
|
||||
|
||||
@@ -644,6 +644,20 @@ class NPCEntity(world: Level) : AgeableMob(CobblemonEntities.NPC, world), Npc, P
|
||||
return InteractionResult.SUCCESS
|
||||
}
|
||||
|
||||
override fun hurt(source: DamageSource, amount: Float): Boolean {
|
||||
if (super.hurt(source, amount)) {
|
||||
callbacks.process(
|
||||
type = EntityCallbacks.HURT,
|
||||
functions = mapOf(
|
||||
"source" to { source.asMoLangValue() },
|
||||
"amount" to { DoubleValue(amount.toDouble()) },
|
||||
)
|
||||
)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override fun recalling(pokemonEntity: PokemonEntity): CompletableFuture<Unit> {
|
||||
playAnimation(RECALL_ANIMATION, pokemonExpressions(pokemonEntity.pokemon))
|
||||
return delayedFuture(seconds = 1.6F)
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
package com.cobblemon.mod.common.entity.npc.ai
|
||||
|
||||
import com.bedrockk.molang.Expression
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
|
||||
import com.cobblemon.mod.common.util.getMemorySafely
|
||||
import com.cobblemon.mod.common.util.mainThreadRuntime
|
||||
import com.cobblemon.mod.common.util.resolveFloat
|
||||
import com.cobblemon.mod.common.util.resolveInt
|
||||
@@ -24,9 +26,11 @@ object MeleeAttackTask {
|
||||
fun create(range: Expression, cooldownTicks: Expression): OneShot<LivingEntity> = BehaviorBuilder.create {
|
||||
it.group(
|
||||
it.present(MemoryModuleType.ATTACK_TARGET),
|
||||
it.absent(MemoryModuleType.ATTACK_COOLING_DOWN)
|
||||
it.absent(MemoryModuleType.ATTACK_COOLING_DOWN),
|
||||
).apply(it) { attackTarget, cooldown ->
|
||||
Trigger { world, entity, _ ->
|
||||
val attackTargetData = entity.brain.getMemorySafely(CobblemonMemories.ATTACK_TARGET_DATA).orElse(null)
|
||||
|
||||
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
|
||||
val range = mainThreadRuntime.resolveFloat(range)
|
||||
val cooldownTicks = mainThreadRuntime.resolveInt(cooldownTicks)
|
||||
@@ -35,6 +39,14 @@ object MeleeAttackTask {
|
||||
if (entity.distanceTo(attackTarget) <= range) {
|
||||
entity.doHurtTarget(attackTarget)
|
||||
cooldown.setWithExpiry(true, cooldownTicks.toLong())
|
||||
if (attackTargetData != null) {
|
||||
if (attackTargetData.dealtHitsUntilDisengage == 1) {
|
||||
attackTargetData.dealtHitsUntilDisengage = 0
|
||||
attackTargetData.disengageAction(entity)
|
||||
} else if (attackTargetData.dealtHitsUntilDisengage > 0){
|
||||
attackTargetData.dealtHitsUntilDisengage--
|
||||
}
|
||||
}
|
||||
true
|
||||
} else {
|
||||
false
|
||||
|
||||
+102
-31
@@ -13,7 +13,9 @@ 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
|
||||
import com.cobblemon.mod.common.CobblemonMemories
|
||||
import com.cobblemon.mod.common.CobblemonSounds
|
||||
import com.cobblemon.mod.common.api.battles.model.PokemonBattle
|
||||
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
|
||||
@@ -28,8 +30,10 @@ import com.cobblemon.mod.common.api.reactive.SimpleObservable
|
||||
import com.cobblemon.mod.common.api.scheduling.Schedulable
|
||||
import com.cobblemon.mod.common.api.scheduling.ScheduledTask
|
||||
import com.cobblemon.mod.common.api.scheduling.SchedulingTracker
|
||||
import com.cobblemon.mod.common.api.text.green
|
||||
import com.cobblemon.mod.common.api.text.red
|
||||
import com.cobblemon.mod.common.api.text.yellow
|
||||
import com.cobblemon.mod.common.battles.BattleBuilder
|
||||
import com.cobblemon.mod.common.battles.BattleCaptureAction
|
||||
import com.cobblemon.mod.common.battles.BattleRegistry
|
||||
import com.cobblemon.mod.common.battles.BattleTypes
|
||||
@@ -72,7 +76,6 @@ import net.minecraft.world.item.Item
|
||||
import net.minecraft.world.level.Level
|
||||
import net.minecraft.world.phys.BlockHitResult
|
||||
import net.minecraft.world.phys.EntityHitResult
|
||||
import net.minecraft.world.phys.HitResult
|
||||
import net.minecraft.world.phys.Vec3
|
||||
import java.util.concurrent.CompletableFuture
|
||||
|
||||
@@ -104,7 +107,8 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
|
||||
override val schedulingTracker = SchedulingTracker()
|
||||
var capturingPokemon: PokemonEntity? = null
|
||||
val captureFuture = CompletableFuture<Boolean>()
|
||||
var captureFuture = CompletableFuture<Boolean>()
|
||||
var capturePostAction: (Boolean) -> Unit = {}
|
||||
var captureState: CaptureState
|
||||
get() = CaptureState.entries[entityData.get(CAPTURE_STATE).toInt()]
|
||||
set(value) { entityData.set(CAPTURE_STATE, value.ordinal.toByte()) }
|
||||
@@ -179,20 +183,28 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
var pokeBall: PokeBall = PokeBalls.POKE_BALL
|
||||
|
||||
override fun onHitBlock(hitResult: BlockHitResult) {
|
||||
if (captureState == CaptureState.NOT) {
|
||||
if (level().isServerSide()) {
|
||||
super.onHitBlock(hitResult)
|
||||
level().sendParticlesServer(ParticleTypes.CLOUD, hitResult.location, 2, hitResult.location.subtract(position()).normalize().scale(-0.1), 0.0)
|
||||
level().playSoundServer(position(), SoundEvents.WOOD_PLACE, pitch = 2.5F)
|
||||
discard()
|
||||
val player = this.owner as? ServerPlayer
|
||||
if (player?.isCreative == false) {
|
||||
spawnAtLocation(defaultItem)
|
||||
when (captureState) {
|
||||
CaptureState.FALL -> {
|
||||
if (level().isServerSide()) {
|
||||
beginCapture()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setNoGravity(false)
|
||||
deltaMovement = Vec3.ZERO
|
||||
CaptureState.NOT -> {
|
||||
if (level().isServerSide()) {
|
||||
super.onHitBlock(hitResult)
|
||||
level().sendParticlesServer(ParticleTypes.CLOUD, hitResult.location, 2, hitResult.location.subtract(position()).normalize().scale(-0.1), 0.0)
|
||||
level().playSoundServer(position(), SoundEvents.WOOD_PLACE, pitch = 2.5F)
|
||||
discard()
|
||||
val player = this.owner as? ServerPlayer
|
||||
if (player?.isCreative == false) {
|
||||
spawnAtLocation(defaultItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
isNoGravity = false
|
||||
deltaMovement = Vec3.ZERO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,7 +267,13 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
owner?.sendSystemMessage(lang("capture.busy", pokemonEntity.exposedSpecies.translatedName).red())
|
||||
return drop()
|
||||
} else if (owner is ServerPlayer && BattleRegistry.getBattleByParticipatingPlayer(owner) != null) {
|
||||
owner.sendSystemMessage(lang("you_in_battle").red())
|
||||
owner.sendSystemMessage(lang("capture.you_in_battle").red())
|
||||
return drop()
|
||||
} else if (owner is ServerPlayer && BattleRegistry.checkBattleLock(owner, pokeBall.ancient)) {
|
||||
owner.sendSystemMessage(lang("capture.you_already_capturing").red())
|
||||
return drop()
|
||||
} else if (pokemonEntity.brain.hasMemoryValue(CobblemonMemories.RECENT_CATCH_ATTEMPT)) {
|
||||
owner?.sendSystemMessage(lang("capture.enraged", pokemonEntity.exposedSpecies.translatedName).red())
|
||||
return drop()
|
||||
}
|
||||
|
||||
@@ -279,6 +297,29 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
super.onHitEntity(hitResult)
|
||||
}
|
||||
|
||||
private fun inBattleCatchAction(battle: PokemonBattle, thrower: LivingEntity, pokemonEntity: PokemonEntity) {
|
||||
val throwerActor = battle.getActor(thrower.uuid)
|
||||
val hitActor = battle.actors.find { it.isForPokemon(pokemonEntity) }
|
||||
val hitBattlePokemon = hitActor?.activePokemon?.find { it.battlePokemon?.effectedPokemon?.entity == pokemonEntity }
|
||||
|
||||
if (throwerActor == null || hitActor == null || hitBattlePokemon == null) {
|
||||
return // Weird, shouldn't be possible
|
||||
}
|
||||
|
||||
val pokemonName = hitBattlePokemon.battlePokemon?.getName() ?: "error".red()
|
||||
|
||||
battle.broadcastChatMessage(
|
||||
lang(
|
||||
"capture.attempted_capture",
|
||||
throwerActor.getName(),
|
||||
pokeBall.item().description,
|
||||
pokemonName
|
||||
).yellow()
|
||||
)
|
||||
battle.dispatchWaiting(2F) { battle.broadcastChatMessage(lang("capture.broke_free", pokemonName).red()) }
|
||||
battle.dispatchGo { throwerActor.forceChoose(ForcePassActionResponse()) }
|
||||
}
|
||||
|
||||
private fun drop() {
|
||||
val owner = owner
|
||||
discard()
|
||||
@@ -352,6 +393,11 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
if (pokemon.pokemon.isWild() && pokemon.isAlive && !captureFuture.isDone) {
|
||||
pokemon.discard()
|
||||
discard()
|
||||
capturePostAction(true)
|
||||
val owner = owner
|
||||
if (owner != null && owner is ServerPlayer) {
|
||||
BattleRegistry.unlockPlayer(owner)
|
||||
}
|
||||
captureFuture.complete(true)
|
||||
val party = player.party()
|
||||
pokemon.pokemon.caughtBall = pokeBall
|
||||
@@ -419,6 +465,11 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
after(seconds = 0.1F) {
|
||||
pokemon.beamMode = 0
|
||||
pokemon.busyLocks.remove(this)
|
||||
capturePostAction(false)
|
||||
val owner = owner
|
||||
if (owner != null && owner is ServerPlayer) {
|
||||
BattleRegistry.unlockPlayer(owner)
|
||||
}
|
||||
captureFuture.complete(false)
|
||||
val ballType =
|
||||
this.pokeBall.name.path.lowercase().replace("_", "")
|
||||
@@ -442,6 +493,10 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
|
||||
private fun attemptCatch(pokemonEntity: PokemonEntity) {
|
||||
pokemonEntity.busyLocks.add(this)
|
||||
val owner = owner
|
||||
if (owner != null && owner is ServerPlayer) {
|
||||
BattleRegistry.lockPlayer(owner, pokeBall.ancient)
|
||||
}
|
||||
val displace = deltaMovement
|
||||
captureState = CaptureState.HIT
|
||||
val mul = if (random.nextBoolean()) 1 else -1
|
||||
@@ -456,7 +511,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
after(seconds = 0.7F) {
|
||||
// Start beaming them up.
|
||||
deltaMovement = Vec3.ZERO
|
||||
setNoGravity(true)
|
||||
isNoGravity = true
|
||||
level().playSoundServer(position(), CobblemonSounds.POKE_BALL_RECALL, volume = 0.6F)
|
||||
pokemonEntity.beamMode = 3
|
||||
}
|
||||
@@ -471,7 +526,7 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
// If it was still falling after a second and a half, just assume it's landed because we can't wait all day.
|
||||
if (captureState == CaptureState.FALL) {
|
||||
deltaMovement = Vec3.ZERO
|
||||
setNoGravity(true)
|
||||
isNoGravity = true
|
||||
setOnGround(true)
|
||||
beginCapture()
|
||||
}
|
||||
@@ -479,25 +534,40 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
}
|
||||
}
|
||||
|
||||
override fun onHit(hitResult: HitResult) {
|
||||
super.onHit(hitResult)
|
||||
if (captureState == CaptureState.FALL && hitResult.type == HitResult.Type.BLOCK) {
|
||||
if (level().isServerSide()) {
|
||||
beginCapture()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We have hit the ground, time to stop falling and start shaking! Calculate capture.
|
||||
fun beginCapture() {
|
||||
// We have hit the ground, time to stop falling and start shaking! Calculate capture.
|
||||
capturingPokemon?.setPositionSafely(position())
|
||||
val capturingPokemon = this.capturingPokemon ?: return
|
||||
capturingPokemon.setPositionSafely(position())
|
||||
|
||||
val thrower = owner as LivingEntity
|
||||
val captureResult = Cobblemon.config.captureCalculator.processCapture(thrower, this, capturingPokemon!!).let {
|
||||
val event = PokeBallCaptureCalculatedEvent(thrower = thrower, pokemonEntity = capturingPokemon!!, pokeBallEntity = this, captureResult = it)
|
||||
val captureResult = Cobblemon.config.captureCalculator.processCapture(thrower, this, capturingPokemon).let {
|
||||
val event = PokeBallCaptureCalculatedEvent(thrower = thrower, pokemonEntity = capturingPokemon, pokeBallEntity = this, captureResult = it)
|
||||
CobblemonEvents.POKE_BALL_CAPTURE_CALCULATED.post(event)
|
||||
event.captureResult
|
||||
}
|
||||
|
||||
if (thrower is ServerPlayer) {
|
||||
val battle = (capturingPokemon.delegate as PokemonServerDelegate).getBattle()
|
||||
if (battle == null && !pokeBall.ancient) {
|
||||
capturingPokemon.brain.setMemoryWithExpiry(CobblemonMemories.RECENT_CATCH_ATTEMPT, thrower.uuid, 30 * 20)
|
||||
this.capturePostAction = {
|
||||
if (!it) {
|
||||
if (this.pokeBall.stealthFactor > 0.0f && this.random.nextFloat() < this.pokeBall.stealthFactor) {
|
||||
thrower.sendSystemMessage(lang("capture.didnt_notice", capturingPokemon.exposedSpecies.translatedName).green())
|
||||
} else {
|
||||
thrower.sendSystemMessage(lang("capture.got_enraged", capturingPokemon.exposedSpecies.translatedName).red())
|
||||
val lead = thrower.party().firstOrNull {it.entity != null}?.uuid
|
||||
if (lead != null) {
|
||||
BattleBuilder.pve(thrower, capturingPokemon, lead).ifSuccessful { battle ->
|
||||
inBattleCatchAction(battle, thrower, capturingPokemon)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var rollsRemaining = captureResult.numberOfShakes
|
||||
if (rollsRemaining == 4) {
|
||||
rollsRemaining--
|
||||
@@ -530,11 +600,12 @@ class EmptyPokeBallEntity : ThrowableItemProjectile, PosableEntity, WaterDragMod
|
||||
}
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun getCurrentPoseType(): PoseType {
|
||||
return PoseType.NONE
|
||||
}
|
||||
|
||||
override fun canUsePortal(allowVehicles: Boolean) = false
|
||||
override fun canUsePortal(allowVehicles: Boolean) = captureState == CaptureState.NOT
|
||||
|
||||
override fun getAddEntityPacket(entityTrackerEntry: ServerEntity): Packet<ClientGamePacketListener> =
|
||||
ClientboundCustomPayloadPacket(
|
||||
|
||||
@@ -36,6 +36,7 @@ import com.cobblemon.mod.common.api.molang.MoLangFunctions.addLivingEntityFuncti
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonEntityFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addPokemonFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.addStandardFunctions
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMoLangValue
|
||||
import com.cobblemon.mod.common.api.molang.MoLangFunctions.setup
|
||||
import com.cobblemon.mod.common.api.molang.ObjectValue
|
||||
import com.cobblemon.mod.common.api.net.serializers.PlatformTypeDataSerializer
|
||||
@@ -1220,6 +1221,14 @@ open class PokemonEntity(
|
||||
// Only touch battle HP for non-wild Pokémon so that quick ball et. al. aren't owerpowered.
|
||||
pokemon.currentHealth = (pokemon.maxHealth * (this.health / this.maxHealth)).toInt()
|
||||
}
|
||||
|
||||
callbacks.process(
|
||||
type = EntityCallbacks.HURT,
|
||||
functions = mapOf(
|
||||
"source" to { source.asMoLangValue() },
|
||||
"amount" to { DoubleValue(amount.toDouble()) },
|
||||
)
|
||||
)
|
||||
true
|
||||
} else false
|
||||
}
|
||||
|
||||
@@ -40,13 +40,15 @@ open class PokeBall(
|
||||
val model2d: ResourceLocation,
|
||||
val model3d: ResourceLocation,
|
||||
val throwPower: Float,
|
||||
val ancient: Boolean
|
||||
val ancient: Boolean,
|
||||
val stealthFactor: Float,
|
||||
) {
|
||||
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("stealth_factor") { DoubleValue(stealthFactor) }
|
||||
// .addFunction("item") { } // requires a registry which is hard
|
||||
|
||||
// This gets attached during item registry
|
||||
|
||||
@@ -67,12 +67,12 @@ class FormPokemonBehaviour {
|
||||
val entityInteract: EntityBehaviour
|
||||
get() = _entityInteract ?: parent.entityInteract
|
||||
|
||||
val dampensVibrations: Boolean
|
||||
get() = _dampensVibrations ?: parent.dampensVibrations
|
||||
|
||||
val blockInteract: BlockBehavior
|
||||
get() = _blockInteract ?: parent.blockInteract
|
||||
|
||||
val dampensVibrations: Boolean
|
||||
get() = _dampensVibrations ?: parent.dampensVibrations
|
||||
|
||||
val combat: CombatBehaviour
|
||||
get() = _combat ?: parent.combat
|
||||
|
||||
|
||||
@@ -35,5 +35,6 @@ class MoveBehaviour {
|
||||
it.addFunction("wander_chance") { DoubleValue(wanderChance) }
|
||||
it.addFunction("wander_speed") { DoubleValue(wanderSpeed) }
|
||||
it.addFunction("can_look") { DoubleValue(canLook) }
|
||||
it.addFunction("can_move") { DoubleValue(walk.canWalk || swim.canSwimInLava || swim.canSwimInWater || fly.canFly) }
|
||||
}
|
||||
}
|
||||
@@ -296,9 +296,7 @@ object PokemonBrain {
|
||||
|
||||
fun onCaptureFailed(pokemonEntity: PokemonEntity, capturer: Entity) {
|
||||
if (pokemonEntity.pokemon.isWild() && pokemonEntity.battleId == null && capturer is LivingEntity) {
|
||||
pokemonEntity.brain.eraseMemory(MemoryModuleType.ATTACK_TARGET)
|
||||
pokemonEntity.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET)
|
||||
pokemonEntity.getBrain().setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, capturer, AVOID_MEMORY_DURATION.sample(pokemonEntity.random).toLong())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2743,7 +2743,11 @@
|
||||
"cobblemon.capture.not_wild": "That %1$s is not a wild Pokémon.",
|
||||
"cobblemon.capture.in_battle": "That %1$s is in a battle and cannot be caught by you.",
|
||||
"cobblemon.capture.busy": "The %1$s is busy!",
|
||||
"cobblemon.capture.enraged": "The %1$s is enraged and can't be caught outside of battle!",
|
||||
"cobblemon.capture.didnt_notice": "The %1$s didn't notice your catch attempt",
|
||||
"cobblemon.capture.got_enraged": "Your capture attempt enraged the %1$s!",
|
||||
"cobblemon.capture.you_in_battle": "You are in a battle.",
|
||||
"cobblemon.capture.you_already_capturing": "You can only capture multiple Pokémon at the same time with ancient balls.",
|
||||
"cobblemon.capture.not_single": "You can only capture wild Pokémon in a 1v1 battle.",
|
||||
"cobblemon.capture.cannot_be_caught": "This Pokémon cannot be captured.",
|
||||
"cobblemon.you_in_battle": "You can't do that while in a battle!",
|
||||
@@ -3754,6 +3758,15 @@
|
||||
"cobblemon.behaviour.pokemon_bee.name": "Pollinates Flowers",
|
||||
"cobblemon.behaviour.pokemon_bee.desc": "Pollinates flowers and adds honey to hives and Saccharine tree leaves.",
|
||||
|
||||
"cobblemon.behaviour.pokemon_neutral_response.name": "Pokémon Response - Neutral",
|
||||
"cobblemon.behaviour.pokemon_neutral_response.desc": "Neutral-style Pokémon responses to triggers. Attacks once and moves away when aggravated. Attacks persistently when attacked while moving away.",
|
||||
|
||||
"cobblemon.behaviour.pokemon_friendly_response.name": "Pokémon Response - Friendly",
|
||||
"cobblemon.behaviour.pokemon_friendly_response.desc": "Friendly-style Pokémon responses to triggers. Flees when attacked. Observes players.",
|
||||
|
||||
"cobblemon.behaviour.callback_hooks.name": "Callback Hooks",
|
||||
"cobblemon.behaviour.callback_hooks.desc": "Runs MoLang scripts when certain events happen, such as when the entity is attacked or when it sees a new entity. Needed for some behaviours to function.",
|
||||
|
||||
"cobblemon.behaviour.pokemon_picks_up_items.name": "Picks Up Items",
|
||||
"cobblemon.behaviour.pokemon_picks_up_items.desc": "The Pokémon will pick up items that it cares about.",
|
||||
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"name": "cobblemon.behaviour.callback_hooks.name",
|
||||
"description": "cobblemon.behaviour.callback_hooks.desc",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activity": "minecraft:core",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
"cobblemon:notify_about_entity_visibility"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,6 +19,19 @@
|
||||
"look_at_target"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "minecraft:idle",
|
||||
"condition": "q.entity.is_pokemon",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:pokemon_wander_control"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -34,7 +34,6 @@
|
||||
"switch_to_panic_when_hostiles_nearby",
|
||||
"calm_down",
|
||||
"flee_attacker",
|
||||
"flee_nearest_hostile",
|
||||
"run_script",
|
||||
"look_in_direction",
|
||||
"path_to_hive",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activity": "minecraft:idle",
|
||||
"activities": ["minecraft:idle", "minecraft:avoid", "minecraft:panic"],
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
{
|
||||
|
||||
@@ -1,6 +1,12 @@
|
||||
{
|
||||
"visible": false,
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:apply_behaviours",
|
||||
"behaviours": [
|
||||
"cobblemon:callback_hooks"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "minecraft:idle",
|
||||
|
||||
@@ -23,12 +23,9 @@
|
||||
}
|
||||
],
|
||||
"1": [
|
||||
"cobblemon:flee_nearest_hostile"
|
||||
],
|
||||
"2": [
|
||||
"cobblemon:walk_away_from_avoid_target"
|
||||
],
|
||||
"3": [
|
||||
"2": [
|
||||
{
|
||||
"type": "cobblemon:wander",
|
||||
"speedMultiplier": {
|
||||
|
||||
@@ -10,7 +10,8 @@
|
||||
"type": "apply_behaviours",
|
||||
"behaviours": [
|
||||
"cobblemon:core",
|
||||
"cobblemon:pokemon_battles"
|
||||
"cobblemon:pokemon_battles",
|
||||
"cobblemon:callback_hooks"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -26,18 +27,6 @@
|
||||
"behaviours": [
|
||||
"cobblemon:pokemon_sleeps"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "minecraft:idle",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:pokemon_wander_control"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+7
-30
@@ -3,37 +3,9 @@
|
||||
"configurations": [
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && q.entity.behaviour.moving.walk.can_walk && !q.entity.behaviour.moving.walk.avoids_land",
|
||||
"condition": "!q.entity.is_in_party",
|
||||
"behaviours": [
|
||||
"cobblemon:wanders"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && q.entity.behaviour.moving.fly.can_fly",
|
||||
"behaviours": [
|
||||
"cobblemon:wanders_hover"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && q.entity.behaviour.moving.swim.can_swim_in_water && !q.entity.behaviour.moving.swim.avoids_water",
|
||||
"behaviours": [
|
||||
"cobblemon:wanders_water"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && (q.entity.behaviour.combat.will_flee && !q.entity.behaviour.combat.will_defend_self)",
|
||||
"behaviours": [
|
||||
"cobblemon:panics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && q.entity.behaviour.combat.will_defend_self",
|
||||
"behaviours": [
|
||||
"cobblemon:retaliates"
|
||||
"cobblemon:pokemon_sleeps"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -49,6 +21,11 @@
|
||||
"behaviours": [
|
||||
"cobblemon:pokemon_picks_up_items"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_memories_and_sensors",
|
||||
"condition": "!q.entity.is_in_party",
|
||||
"memories": ["cobblemon:recent_catch_attempt"]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"visible": false,
|
||||
"configurations": [
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && q.entity.behaviour.moving.walk.can_walk && !q.entity.behaviour.moving.walk.avoids_land",
|
||||
"behaviours": [
|
||||
"cobblemon:wanders"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && !q.entity.behaviour.moving.walk.can_walk && q.entity.behaviour.moving.fly.can_fly",
|
||||
"behaviours": [
|
||||
"cobblemon:wanders_hover"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "apply_behaviours",
|
||||
"condition": "!q.entity.is_in_party && q.entity.behaviour.moving.walk.avoids_land && q.entity.behaviour.moving.swim.can_swim_in_water && !q.entity.behaviour.moving.swim.avoids_water",
|
||||
"behaviours": [
|
||||
"cobblemon:wanders_water"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"_comment": "Used to give the basics of avoidance behaviour for Pokémon.",
|
||||
"visible": false,
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:add_memories_and_sensors",
|
||||
"memories": [
|
||||
"minecraft:avoid_target",
|
||||
"cobblemon:avoid_target_data"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activity": "minecraft:avoid",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
"cobblemon:follow_walk_target"
|
||||
],
|
||||
"1": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:wander_avoid"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"visible": false,
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:add_memories_and_sensors",
|
||||
"memories": [
|
||||
"minecraft:visible_mobs"
|
||||
],
|
||||
"sensors": [
|
||||
"minecraft:nearest_living_entities"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activity": "minecraft:panic",
|
||||
"tasksByPriority": {
|
||||
"0": [
|
||||
"cobblemon:follow_walk_target"
|
||||
],
|
||||
"1": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:wander_avoid"
|
||||
}
|
||||
],
|
||||
"2": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:wander_panic"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"name": "cobblemon.behaviour.pokemon_friendly_response.name",
|
||||
"description": "cobblemon.behaviour.pokemon_friendly_response.desc",
|
||||
"onAdd": [
|
||||
"q.entity.add_callback('cobblemon:hurt', 'cobblemon:flee_aggressor');",
|
||||
"q.entity.add_callback('cobblemon:saw_entity', 'cobblemon:begin_to_observe_from_distance');",
|
||||
"q.entity.add_callback('cobblemon:lost_sight_of_entity', 'cobblemon:stop_observing_from_distance');"
|
||||
],
|
||||
"onRemove": [
|
||||
"q.entity.remove_callback('cobblemon:hurt', 'cobblemon:flee_aggressor');",
|
||||
"q.entity.remove_callback('cobblemon:saw_entity', 'cobblemon:begin_to_observe_from_distance');",
|
||||
"q.entity.remove_callback('cobblemon:lost_sight_of_entity', 'cobblemon:stop_observing_from_distance');"
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:apply_behaviours",
|
||||
"behaviours": [
|
||||
"cobblemon:pokemon_panics"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_memories_and_sensors",
|
||||
"memories": [
|
||||
"minecraft:avoid_target",
|
||||
"cobblemon:avoid_target_data",
|
||||
"minecraft:flee_target"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activity": "minecraft:panic",
|
||||
"tasksByPriority": {
|
||||
"2": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:stop_avoiding_when_target_gone"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"name": "cobblemon.behaviour.pokemon_neutral_response.name",
|
||||
"description": "cobblemon.behaviour.pokemon_neutral_response.desc",
|
||||
"onAdd": [
|
||||
"q.entity.add_callback('cobblemon:hurt', 'cobblemon:attack_aggressor_once_then_avoid');",
|
||||
"q.entity.add_callback('cobblemon:hit_by_pokeball', 'cobblemon:attack_aggressor_once_then_avoid');"
|
||||
],
|
||||
"onRemove": [
|
||||
"q.entity.remove_callback('cobblemon:hurt', 'cobblemon:attack_aggressor_once_then_avoid');",
|
||||
"q.entity.remove_callback('cobblemon:hit_by_pokeball', 'cobblemon:attack_aggressor_once_then_avoid');"
|
||||
],
|
||||
"configurations": [
|
||||
{
|
||||
"type": "cobblemon:apply_behaviours",
|
||||
"behaviours": [
|
||||
"cobblemon:pokemon_avoids"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_memories_and_sensors",
|
||||
"memories": [
|
||||
"minecraft:attack_target",
|
||||
"cobblemon:attack_target_data",
|
||||
"cobblemon:recent_catch_attempt"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activity": "minecraft:avoid",
|
||||
"tasksByPriority": {
|
||||
"2": [
|
||||
{
|
||||
"type": "cobblemon:run_script",
|
||||
"script": "cobblemon:stop_avoiding_when_target_gone"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -3,11 +3,11 @@
|
||||
"description": "cobblemon.behaviour.wanders.desc",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activities": ["minecraft:idle", "minecraft:avoid", "minecraft:panic"],
|
||||
"tasksByPriority": {
|
||||
"7": [
|
||||
"wander"
|
||||
"cobblemon:wander"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"description": "cobblemon.behaviour.wanders_air.desc",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activities": ["minecraft:idle", "minecraft:avoid", "minecraft:panic"],
|
||||
"tasksByPriority": {
|
||||
"7": [
|
||||
"cobblemon:air_wander"
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
"description": "cobblemon.behaviour.wanders_hover.desc",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activities": ["minecraft:idle", "minecraft:avoid", "minecraft:panic"],
|
||||
"tasksByPriority": {
|
||||
"7": [
|
||||
{
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
"description": "cobblemon.behaviour.wanders_water.desc",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "add_tasks_to_activity",
|
||||
"activity": "idle",
|
||||
"type": "cobblemon:add_tasks_to_activity",
|
||||
"activities": ["minecraft:idle", "minecraft:avoid", "minecraft:panic"],
|
||||
"tasksByPriority": {
|
||||
"7": [
|
||||
"water_wander"
|
||||
"cobblemon:water_wander"
|
||||
],
|
||||
"9": [
|
||||
{
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
t.aggressor = q.run_script('cobblemon:get_aggressor');
|
||||
t.aggressor == 0 ? {
|
||||
return 0;
|
||||
};
|
||||
t.activity = q.entity.activity;
|
||||
q.entity.is_doing_activity('cobblemon:battling', 'cobblemon:fight', 'cobblemon:sleep') ? {
|
||||
return 0;
|
||||
};
|
||||
q.entity.is_doing_activity('cobblemon:avoid') ? {
|
||||
'Oh we are very angry now';
|
||||
q.entity.set_entity_memory('minecraft:attack_target', t.aggressor);
|
||||
q.entity.erase_memory('cobblemon:path_cooldown', 'cobblemon:attack_target_data');
|
||||
q.entity.set_activity('minecraft:fight');
|
||||
} : {
|
||||
t.attack_target_data = q.entity.get_attack_target_data_memory();
|
||||
t.attack_target_data.set_disengage_action('cobblemon:move_away_from_attack_target');
|
||||
t.attack_target_data.set_dealt_hits_until_disengage(1);
|
||||
q.entity.set_entity_memory('minecraft:attack_target', t.aggressor);
|
||||
q.entity.erase_memory('cobblemon:path_cooldown');
|
||||
q.entity.set_activity('minecraft:fight');
|
||||
};
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
t.seen_entity = q.other_entity;
|
||||
'Do some stuff if we are in the right activity or whatever';
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
t.aggressor = q.run_script('cobblemon:get_aggressor');
|
||||
t.aggressor == 0 ? {
|
||||
return 0;
|
||||
};
|
||||
q.entity.set_entity_memory('minecraft:avoid_target', t.aggressor);
|
||||
q.entity.erase_memory('cobblemon:path_cooldown');
|
||||
q.entity.set_activity('minecraft:panic');
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
t.attack_target = q.entity.get_entity_memory('minecraft:attack_target');
|
||||
t.attack_target == 0 ? {
|
||||
return 0;
|
||||
};
|
||||
q.entity.erase_memory('minecraft:attack_target');
|
||||
q.entity.set_entity_memory('minecraft:avoid_target', t.attack_target);
|
||||
q.entity.set_activity('minecraft:avoid');
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
t.lost_entity = q.other_entity;
|
||||
'Some logic';
|
||||
@@ -0,0 +1,5 @@
|
||||
t.avoid_target = q.entity.get_entity_memory('minecraft:avoid_target');
|
||||
t.avoid_target == 0 || !q.entity.is_nearby_entity(t.avoid_target) ? {
|
||||
q.entity.reset_activity();
|
||||
return 0;
|
||||
};
|
||||
@@ -0,0 +1,10 @@
|
||||
q.pokeball != 0 ? {
|
||||
return q.pokeball.thrower;
|
||||
};
|
||||
q.source != 0 ? {
|
||||
return q.source.entity;
|
||||
};
|
||||
q.other_entity != 0 ? {
|
||||
return q.other_entity;
|
||||
};
|
||||
return 0;
|
||||
@@ -0,0 +1,23 @@
|
||||
'Takes the avoid target and uses their position as a place to avoid being near. This goes for move away and for panic fleeing.';
|
||||
|
||||
q.entity.has_memory_value('minecraft:avoid_target') == 0 ? {
|
||||
return 0;
|
||||
};
|
||||
|
||||
t.wander_control = q.entity.get_wander_control_memory();
|
||||
t.avoid_target_data = q.entity.get_avoid_target_data_memory();
|
||||
t.avoid_entity = q.entity.get_entity_memory('minecraft:avoid_target');
|
||||
t.avoid_entity == 0 || t.avoid_entity.world.id != q.entity.world.id ? {
|
||||
return 0;
|
||||
};
|
||||
|
||||
t.distance_to_entity = t.avoid_entity.distance_to_entity(q.entity);
|
||||
t.distance_to_entity - 8 > 24 ? {
|
||||
t.min_distance = 24;
|
||||
} : {
|
||||
t.min_distance = t.distance_to_entity + 8;
|
||||
};
|
||||
|
||||
t.wander_control.set_center(t.avoid_entity.x, t.avoid_entity.y, t.avoid_entity.z, t.min_distance, 9999);
|
||||
t.wander_control.set_path_cooldown_ticks(20);
|
||||
t.wander_control.set_wander_chance(1/20);
|
||||
@@ -0,0 +1,12 @@
|
||||
'Panicking means moving very fast if the avoid target is nearby.';
|
||||
|
||||
t.wander_control = q.entity.get_wander_control_memory();
|
||||
t.avoid_target_data = q.entity.get_avoid_target_data_memory();
|
||||
t.avoid_entity = q.entity.get_entity_memory('minecraft:avoid_target');
|
||||
t.avoid_entity != 0 && q.entity.distance_to_entity(t.avoid_entity) > 20 ? {
|
||||
return 0;
|
||||
};
|
||||
|
||||
t.wander_control.set_speed_multiplier(1.4);
|
||||
t.wander_control.set_wander_chance(1);
|
||||
t.wander_control.set_path_cooldown_ticks(10);
|
||||
@@ -17,9 +17,9 @@ loom {
|
||||
val clientConfig = runConfigs.getByName("client")
|
||||
clientConfig.runDir = "runClient"
|
||||
// clientConfig.vmArg("-Dmixin.debug=true")
|
||||
// clientConfig.programArg("--username=AshKetchum")
|
||||
clientConfig.programArg("--username=AshKetchum")
|
||||
//This is AshKetchum's UUID so you get an Ash Ketchum skin
|
||||
// clientConfig.programArg("--uuid=93e4e551-589a-41cb-ab2d-435266c8e035")
|
||||
clientConfig.programArg("--uuid=93e4e551-589a-41cb-ab2d-435266c8e035")
|
||||
val serverConfig = runConfigs.getByName("server")
|
||||
serverConfig.runDir = "runServer"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user