Pokemon that don't defend themselves will instead run for their life when hurt

This commit is contained in:
Paul
2025-09-06 14:15:52 +10:00
parent f771248936
commit 67f5259742
11 changed files with 112 additions and 48 deletions
@@ -117,7 +117,7 @@ Example behaviour (`uses_healing_machine.json`):
"configurations": [
{
"type": "add_tasks_to_activity",
"activity": "idle",
"activity": "minecraft:idle",
"tasksByPriority": {
"1": [
"heal_using_healing_machine",
@@ -9,44 +9,36 @@
package com.cobblemon.mod.common.api.ai.config.task
import com.cobblemon.mod.common.api.ai.BehaviourConfigurationContext
import com.cobblemon.mod.common.api.ai.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.ExpressionOrEntityVariable
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.withQueryValue
import com.cobblemon.mod.common.api.npc.configuration.MoLangConfigVariable
import com.cobblemon.mod.common.entity.ai.FleeFromAttackerTask
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class FleeAttackerTaskConfig : SingleTaskConfig {
var condition = booleanVariable(SharedEntityVariables.FEAR_CATEGORY, "flee_attacker", true).asExpressible()
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.5).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
val avoidDurationTicks: ExpressionOrEntityVariable = Either.left("600".asExpression())
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun getVariables(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): List<MoLangConfigVariable> {
return listOf(avoidDurationTicks).asVariables()
}
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
): BehaviorControl<in LivingEntity> {
behaviourConfigurationContext.addMemories(
MemoryModuleType.HURT_BY_ENTITY,
MemoryModuleType.AVOID_TARGET
)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
return FleeFromAttackerTask.create(avoidDurationTicks.asExpression())
}
}
@@ -162,11 +162,11 @@ on the entity type and this piggy-backs off the Minecraft list of aggressive mob
The 'calm_down' type creates a task that will make the entity calm down when it is no longer being hurt and is far from hostiles and its attacker.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
### flee_attacker
The 'flee_attacker' type creates a task that will make the entity flee from its attacker when it is hurt.
### walk_away_from_avoid_target
The 'flee_attacker' type creates a task that will make the entity flee from its avoid target.
- `condition`: A MoLang expression (with `q.entity` as the entity) that determines if this entire task should exist on the entity or not. This runs at the time of entity creation, not on tick.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the attacker. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the attacker that the entity should try to maintain. Defaults to 9.
- `speedMultiplier`: A MoLang expression that determines the speed multiplier for fleeing from the avoid target. Defaults to 0.5 which is an elevated speed to usual.
- `desiredDistance`: A MoLang expression that determines the distance from the target that the entity should try to maintain. Defaults to 9.
### flee_nearest_hostile
The 'flee_nearest_hostile' type creates a task that will make the entity flee from the nearest hostile entity when it is hurt.
@@ -78,8 +78,9 @@ interface TaskConfig {
cobblemonResource("switch_to_panic_when_hurt") to SwitchToPanicWhenHurtTaskConfig::class.java,
cobblemonResource("switch_to_panic_when_hostiles_nearby") to SwitchToPanicWhenHostilesNearbyTaskConfig::class.java,
cobblemonResource("calm_down") to CalmDownTaskConfig::class.java,
cobblemonResource("flee_attacker") to FleeAttackerTaskConfig::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,
cobblemonResource("look_in_direction") to LookInDirectionTaskConfig::class.java,
@@ -0,0 +1,53 @@
/*
* 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.WrapperLivingEntityTask
import com.cobblemon.mod.common.api.ai.asVariables
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_DESIRED_DISTANCE
import com.cobblemon.mod.common.api.ai.config.task.SharedEntityVariables.FLEE_SPEED_MULTIPLIER
import com.cobblemon.mod.common.util.asExpression
import com.mojang.datafixers.util.Either
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.PathfinderMob
import net.minecraft.world.entity.ai.behavior.BehaviorControl
import net.minecraft.world.entity.ai.behavior.SetWalkTargetAwayFrom
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.SensorType
class WalkAwayFromAvoidTargetTaskConfig : SingleTaskConfig {
var condition: ExpressionOrEntityVariable = Either.left("true".asExpression())
var speedMultiplier = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_SPEED_MULTIPLIER, 0.7).asExpressible()
var desiredDistance = numberVariable(SharedEntityVariables.FEAR_CATEGORY, FLEE_DESIRED_DISTANCE, 9).asExpressible()
override fun getVariables(entity: LivingEntity, behaviourConfigurationContext: BehaviourConfigurationContext) = listOf(
condition,
speedMultiplier,
desiredDistance
).asVariables()
override fun createTask(
entity: LivingEntity,
behaviourConfigurationContext: BehaviourConfigurationContext
): BehaviorControl<in LivingEntity>? {
if (!condition.resolveBoolean(behaviourConfigurationContext.runtime) || entity !is PathfinderMob) return null
behaviourConfigurationContext.addMemories(MemoryModuleType.HURT_BY_ENTITY, MemoryModuleType.WALK_TARGET)
behaviourConfigurationContext.addSensors(SensorType.HURT_BY)
val speedMultiplier = speedMultiplier.resolveFloat(behaviourConfigurationContext.runtime)
val desiredDistance = desiredDistance.resolveInt(behaviourConfigurationContext.runtime)
return WrapperLivingEntityTask(
SetWalkTargetAwayFrom.entity(MemoryModuleType.HURT_BY_ENTITY, speedMultiplier, desiredDistance, false),
PathfinderMob::class.java
)
}
}
@@ -8,6 +8,11 @@
package com.cobblemon.mod.common.entity.ai
import com.bedrockk.molang.Expression
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.util.mainThreadRuntime
import com.cobblemon.mod.common.util.resolveInt
import com.cobblemon.mod.common.util.withQueryValue
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.behavior.OneShot
import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder
@@ -15,7 +20,7 @@ import net.minecraft.world.entity.ai.behavior.declarative.Trigger
import net.minecraft.world.entity.ai.memory.MemoryModuleType
object FleeFromAttackerTask {
fun create(): OneShot<LivingEntity> {
fun create(avoidDurationTicks: Expression): OneShot<LivingEntity> {
return BehaviorBuilder.create {
it.group(
it.present(MemoryModuleType.HURT_BY_ENTITY),
@@ -27,7 +32,9 @@ object FleeFromAttackerTask {
if (avoidTarget != null && avoidTarget == hurtByEntity.uuid) {
return@Trigger false
}
entity.brain.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, hurtByEntity, 30 * 20)
mainThreadRuntime.withQueryValue("entity", entity.asMostSpecificMoLangValue())
val avoidDurationTicks = mainThreadRuntime.resolveInt(avoidDurationTicks)
entity.brain.setMemoryWithExpiry(MemoryModuleType.AVOID_TARGET, hurtByEntity, avoidDurationTicks.toLong())
return@Trigger true
}
}
@@ -26,7 +26,7 @@ object GoToSleepTask {
it.absent(MemoryModuleType.WALK_TARGET),
it.registered(CobblemonMemories.POKEMON_DROWSY)
).apply(it) { _, pokemonDrowsy ->
Trigger { world, entity, _ ->
Trigger { _, entity, _ ->
val hasSleepStatus = entity.pokemon.status?.status === Statuses.SLEEP
if (entity.behaviour.resting.canSleep && ((it.tryGet(pokemonDrowsy).orElse(false) && entity.canSleepAt(entity.blockPosition().below())) || hasSleepStatus) && entity.pokemon.storeCoordinates.get()?.store !is PartyStore) {
if (!hasSleepStatus) {
@@ -13,7 +13,7 @@ import com.cobblemon.mod.common.api.molang.ObjectValue
class CombatBehaviour {
var willDefendSelf = false
var willFlee = false
var willFlee = true
var willDefendOwner = false
var fightsMelee = true
@@ -28,6 +28,7 @@ import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.DrowsySensor
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.*
import com.cobblemon.mod.common.pokemon.Pokemon
import com.cobblemon.mod.common.util.asExpression
import com.cobblemon.mod.common.util.toDF
import com.google.common.collect.ImmutableList
import com.google.common.collect.ImmutableSet
@@ -59,6 +60,11 @@ object PokemonBrain {
private val ADULT_FOLLOW_RANGE = UniformInt.of(5, 16)
private val AVOID_MEMORY_DURATION = TimeUtil.rangeOfSeconds(5, 20)
/**
* The things that absolutely should be on all pokemon can be put in here. Things that make no sense
* without specific activities/Behaviours should be given as part of those activities. Some of the things
* in this list currently violate this rule but it's whatever, I'll get to them later.
*/
val SENSORS: Collection<SensorType<out Sensor<in PokemonEntity>>> = listOf(
SensorType.NEAREST_LIVING_ENTITIES,
SensorType.HURT_BY,
@@ -206,7 +212,7 @@ object PokemonBrain {
if (pokemon.form.behaviour.combat.willDefendSelf) {
add(0 toDF GetAngryAtAttackerTask.create())
} else {
add(0 toDF FleeFromAttackerTask.create())
add(0 toDF FleeFromAttackerTask.create("600".asExpression()))
}
add(0 toDF StopBeingAngryIfTargetDead.create())
@@ -3,29 +3,34 @@
"description": "cobblemon.behaviour.panics.desc",
"configurations": [
{
"type": "add_tasks_to_activity",
"activity": "idle",
"type": "cobblemon:add_tasks_to_activity",
"activity": "minecraft:idle",
"tasksByPriority": {
"0": ["switch_to_panic_when_hurt"]
"0": ["cobblemon:flee_attacker"],
"10": ["cobblemon:switch_to_panic_when_hurt"]
}
},
{
"type": "add_tasks_to_activity",
"activity": "panic",
"type": "cobblemon:add_tasks_to_activity",
"activity": "minecraft:panic",
"tasksByPriority": {
"0": [
"calm_down",
"follow_walk_target"
"cobblemon:flee_attacker",
{
"type": "cobblemon:activity_change",
"activity": "minecraft:idle",
"condition": "q.entity.lacks_memory_value('minecraft:avoid_target')"
}
],
"1": [
"flee_nearest_hostile"
"cobblemon:flee_nearest_hostile"
],
"2": [
"flee_attacker"
"cobblemon:walk_away_from_avoid_target"
],
"3": [
{
"type": "wander",
"type": "cobblemon:wander",
"speedMultiplier": {
"variableName": "flee_speed_multiplier",
"category": "cobblemon.entity.variable.category.fear",
@@ -31,7 +31,7 @@
},
{
"type": "apply_behaviours",
"condition": "!q.entity.is_in_party && q.entity.behaviour.combat.will_flee",
"condition": "!q.entity.is_in_party && (q.entity.behaviour.combat.will_flee && !q.entity.behaviour.combat.will_defend_self)",
"behaviours": [
"cobblemon:panics"
]