Migrated the pickup items for foxes and stuff to the config struct

This commit is contained in:
Paul
2025-08-10 19:13:35 +10:00
parent 99b99a803c
commit 1936c8a0be
17 changed files with 93 additions and 59 deletions
@@ -29,6 +29,6 @@ class EatHeldItemTaskConfig : SingleTaskConfig {
behaviourConfigurationContext.addMemories(
CobblemonMemories.IS_CONSUMING_ITEM,
)
return EatHeldItemTask().wrapped<PokemonEntity>()
return EatHeldItemTask(entity).wrapped<PokemonEntity>()
}
}
@@ -38,7 +38,9 @@ interface MoLangScriptingEntity {
var behavioursAreCustom: Boolean
val behaviours: MutableList<ResourceLocation>
val registeredVariables: MutableList<MoLangConfigVariable>
/** Configuration properties that are managed and reset prior to entity behaviour initialization. */
var config: VariableStruct
/** Data that is persisted. */
var data: VariableStruct
var callbacks: EntityCallbacks
@@ -76,6 +76,7 @@ import com.cobblemon.mod.common.entity.generic.GenericBedrockEntity
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokeball.EmptyPokeBallEntity
import com.cobblemon.mod.common.entity.pokemon.ai.PokemonMoveControl
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonItemSensor
import com.cobblemon.mod.common.entity.pokemon.effects.EffectTracker
import com.cobblemon.mod.common.entity.pokemon.effects.IllusionEffect
import com.cobblemon.mod.common.net.messages.client.OpenBehaviourEditorPacket
@@ -96,6 +97,7 @@ import com.cobblemon.mod.common.pokemon.activestate.InactivePokemonState
import com.cobblemon.mod.common.pokemon.activestate.SentOutState
import com.cobblemon.mod.common.pokemon.activestate.ShoulderedState
import com.cobblemon.mod.common.pokemon.ai.FormPokemonBehaviour
import com.cobblemon.mod.common.pokemon.ai.ObtainableItem
import com.cobblemon.mod.common.pokemon.ai.PokemonBrain
import com.cobblemon.mod.common.pokemon.evolution.variants.ItemInteractionEvolution
import com.cobblemon.mod.common.pokemon.feature.StashHandler
@@ -1049,9 +1051,10 @@ open class PokemonEntity(
}
override fun wantsToPickUp(stack: ItemStack): Boolean {
return this.canHoldItem(stack) && (behaviour.itemInteract.getMatchingEntry(stack)?.pickupPriority
?: 0) > 0
val pickupItems = config.getObjectList<ObtainableItem>(PokemonItemSensor.PICKUP_ITEMS)
return this.canHoldItem(stack) && (pickupItems.findMatchingEntry(stack)?.pickupPriority ?: 0) > 0
}
override fun mobInteract(player: Player, hand: InteractionHand): InteractionResult {
if (!this.isBattling && this.isBattleClone()) {
return InteractionResult.FAIL
@@ -9,33 +9,39 @@
package com.cobblemon.mod.common.entity.pokemon.ai.sensors
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.ai.ObtainableItem
import com.cobblemon.mod.common.util.findMatchingEntry
import com.cobblemon.mod.common.util.getObjectList
import com.google.common.collect.ImmutableSet
import java.util.Optional
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.Sensor
import net.minecraft.world.entity.item.ItemEntity
import net.minecraft.world.level.GameRules
import java.util.*
import java.util.function.Predicate
class PokemonItemSensor(
private val width: Double = 16.0, // TODO: Can we configure sensors dynamically?
private val width: Double = 16.0, // TODO: Can we configure sensors dynamically? // Nope. Could be done with a config struct option but it's a bit crass.
private val height: Double = 8.0,
private val maxTravelDistance: Double = 16.0,
) : Sensor<PokemonEntity>(30) {
companion object {
const val PICKUP_ITEMS = "pickup_items"
}
override fun requires(): MutableSet<MemoryModuleType<*>?> {
return ImmutableSet.of<MemoryModuleType<*>?>(MemoryModuleType.NEAREST_VISIBLE_WANTED_ITEM)
}
override fun doTick(level: ServerLevel, entity: PokemonEntity) {
val pickupItems = entity.config.getObjectList<ObtainableItem>(PICKUP_ITEMS)
if (!level.gameRules.getBoolean(GameRules.RULE_MOBGRIEFING) || !entity.pokemon.canDropHeldItem) {
// Mob griefing is disabled or the Pokemon cannot swap out its item, so don't bother to search
return
}
val heldItemValue = entity.behaviour.itemInteract.getItemPriority(entity.pokemon.heldItem())
if (heldItemValue >= entity.behaviour.itemInteract.highestPriorityItem) {
val heldItemValue = pickupItems.findMatchingEntry(entity.pokemon.heldItem())?.pickupPriority ?: 0
if (heldItemValue >= (pickupItems.maxOfOrNull { it.pickupPriority } ?: 0)) {
// It's already holding the highest value item it can have, no need to look for better.
return
}
@@ -11,8 +11,11 @@ package com.cobblemon.mod.common.entity.pokemon.ai.tasks
import com.cobblemon.mod.common.CobblemonMemories
import com.cobblemon.mod.common.api.molang.MoLangFunctions.asMostSpecificMoLangValue
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.entity.pokemon.ai.sensors.PokemonItemSensor.Companion.PICKUP_ITEMS
import com.cobblemon.mod.common.entity.pokemon.ai.tasks.MoveToItemTask.runtime
import com.cobblemon.mod.common.util.asExpressionLike
import com.cobblemon.mod.common.pokemon.ai.ObtainableItem
import com.cobblemon.mod.common.util.findMatchingEntry
import com.cobblemon.mod.common.util.getObjectList
import com.cobblemon.mod.common.util.resolve
import com.cobblemon.mod.common.util.withQueryValue
import com.google.common.collect.ImmutableMap
@@ -29,7 +32,7 @@ import net.minecraft.world.item.ItemStack
import net.minecraft.world.item.Items
class EatHeldItemTask : Behavior<PokemonEntity>(
class EatHeldItemTask(entity: PokemonEntity) : Behavior<PokemonEntity>(
ImmutableMap.of(
MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT
)
@@ -41,6 +44,7 @@ class EatHeldItemTask : Behavior<PokemonEntity>(
}
private var timelastEaten: Long = 0
val pickupItems = entity.config.getObjectList<ObtainableItem>(PICKUP_ITEMS)
override fun checkExtraStartConditions(world: ServerLevel, entity: PokemonEntity): Boolean {
if (timelastEaten + COOLDOWN > world.gameTime) {
@@ -55,7 +59,7 @@ class EatHeldItemTask : Behavior<PokemonEntity>(
}
private fun canEat(item: ItemStack, entity: PokemonEntity): Boolean {
return item.has(DataComponents.FOOD) || item.item == Items.POTION || entity.behaviour.itemInteract.getOnUseEffect(item) != null
return item.has(DataComponents.FOOD) || item.item == Items.POTION || pickupItems.findMatchingEntry(item)?.onUseEffect != null
}
override fun start(world: ServerLevel, entity: PokemonEntity, time: Long) {
@@ -70,7 +74,7 @@ class EatHeldItemTask : Behavior<PokemonEntity>(
override fun tick(world: ServerLevel, entity: PokemonEntity, time: Long) {
if (!world.isClientSide && entity.isAlive && entity.isEffectiveAi) {
val itemStack: ItemStack = entity.pokemon.heldItem()
val itemConfig = entity.behaviour.itemInteract.getMatchingEntry(itemStack)
val itemConfig = pickupItems.findMatchingEntry(itemStack)
if (canEat(itemStack, entity)) {
if (this.timelastEaten + MAX_DURATION <= time) {
var resultItemStack = itemStack.finishUsingItem(world, entity)
@@ -259,7 +259,6 @@ class FormData(
this.evolutions.size
this._lightingData?.let { this._lightingData = it.copy(lightLevel = it.lightLevel.coerceIn(0, 15)) }
behaviour.herd.initialize()
behaviour.itemInteract.initialize()
return this
}
@@ -31,8 +31,8 @@ import com.cobblemon.mod.common.api.pokemon.evolution.PreEvolution
import com.cobblemon.mod.common.api.pokemon.experience.ExperienceGroups
import com.cobblemon.mod.common.api.pokemon.moves.Learnset
import com.cobblemon.mod.common.api.pokemon.stats.Stat
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.riding.RidingProperties
import com.cobblemon.mod.common.api.storage.InvalidSpeciesException
import com.cobblemon.mod.common.api.types.ElementalType
import com.cobblemon.mod.common.api.types.ElementalTypes
import com.cobblemon.mod.common.entity.PoseType.Companion.FLYING_POSES
@@ -190,7 +190,6 @@ class Species : ClientDataSynchronizer<Species>, ShowdownIdentifiable {
this.preEvolution?.form
this.evolutions.size
behaviour.herd.initialize()
behaviour.itemInteract.initialize()
}
// Ran after initialize due to us creating a Pokémon here which requires all the properties in #initialize to be present for both this and the results, this is the easiest way to quickly resolve species + form
@@ -8,47 +8,16 @@
package com.cobblemon.mod.common.pokemon.ai
import com.cobblemon.mod.common.api.molang.ExpressionLike
import com.cobblemon.mod.common.api.molang.ObjectValue
import com.cobblemon.mod.common.util.server
import kotlin.math.max
import net.minecraft.world.item.ItemStack
import com.cobblemon.mod.common.util.asArrayValue
class ItemBehavior {
private val desiredItems = mutableListOf<ObtainableItem>()
@Transient
var highestPriorityItem = 0
fun getMatchingEntry(stack: ItemStack): ObtainableItem? {
val registryAccess = server()?.registryAccess() ?: return null
return if (stack == ItemStack.EMPTY) {
null
} else {
desiredItems.find { it.item?.isItemObtainable(registryAccess, stack) != false }
}
}
private val desiredItems = listOf<ObtainableItem>()
@Transient
val struct = ObjectValue(this).also {
it.addFunction("add_pickup_item") { params ->
addPickupItem(params.get<ObjectValue<ObtainableItem>>(0).obj)
it.addFunction("pickup_items") {
return@addFunction desiredItems.asArrayValue(ObtainableItem::struct)
}
}
fun getItemPriority(stack: ItemStack): Int {
return getMatchingEntry(stack)?.pickupPriority ?: 0
}
fun getOnUseEffect(stack: ItemStack): ExpressionLike? {
return getMatchingEntry(stack)?.onUseEffect
}
fun addPickupItem(entry: ObtainableItem) {
desiredItems.add(entry)
highestPriorityItem = max(entry.pickupPriority, highestPriorityItem)
}
fun initialize() {
highestPriorityItem = desiredItems.maxOfOrNull { it.pickupPriority } ?: 0
}
}
@@ -12,6 +12,7 @@ import com.cobblemon.mod.common.Cobblemon
import com.cobblemon.mod.common.client.render.models.blockbench.PosableState
import com.cobblemon.mod.common.entity.npc.NPCEntity
import com.cobblemon.mod.common.entity.pokemon.PokemonEntity
import com.cobblemon.mod.common.pokemon.ai.ObtainableItem
import net.minecraft.client.Minecraft
import net.minecraft.client.gui.screens.Screen
import java.util.UUID
@@ -27,6 +28,7 @@ import net.minecraft.network.chat.Component
import net.minecraft.resources.ResourceLocation
import net.minecraft.world.InteractionHand
import net.minecraft.world.entity.EquipmentSlot
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.pathfinder.Node
import net.minecraft.world.level.pathfinder.Path
import net.minecraft.world.phys.shapes.VoxelShape
@@ -202,4 +204,14 @@ fun Path.deleteNode(index: Int) {
for (node in correctNodes) {
this.replaceNode(i++, node)
}
}
fun Collection<ObtainableItem>.findMatchingEntry(stack: ItemStack): ObtainableItem? {
val server = server() ?: return null
val registryAccess = server.registryAccess()
return if (stack == ItemStack.EMPTY) {
null
} else {
this.find { it.item?.isItemObtainable(registryAccess, stack) != false }
}
}
@@ -16,6 +16,7 @@ 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.ContextStruct
import com.bedrockk.molang.runtime.struct.VariableStruct
import com.bedrockk.molang.runtime.value.DoubleValue
import com.bedrockk.molang.runtime.value.MoValue
import com.cobblemon.mod.common.Cobblemon
@@ -203,6 +204,22 @@ fun ArrayStruct.getString(index: Int) = map["$index"]!!.asString()
fun ArrayStruct.asBlockPos() = BlockPos(getDouble(0).toInt(), getDouble(1).toInt(), getDouble(2).toInt())
fun ArrayStruct.asVec3d() = Vec3(getDouble(0), getDouble(1), getDouble(2))
fun <T> VariableStruct.getObject(name: String): T? {
val value = map[name] ?: return null
if (value !is ObjectValue<*>) {
return null
}
return value.obj as? T
}
fun <T> VariableStruct.getObjectList(name: String): List<T> {
val value = map[name] ?: return emptyList()
if (value !is ArrayStruct) {
return emptyList()
}
return value.map.values.mapNotNull { (it as? ObjectValue<T>)?.obj }
}
fun MoLangRuntime.clone(): MoLangRuntime {
val runtime = MoLangRuntime()
runtime.environment.cloneFrom(environment)
@@ -26,6 +26,10 @@
}
]
}
},
{
"type": "script",
"script": "cobblemon:add_pickup_items"
}
]
}
@@ -5,15 +5,16 @@ q.entity.behaviour.block_interact.set_immune_to_sweet_berry_bush_block(true);
t.fox_food = q.create_pickup_item('#minecraft:fox_food', 5);
t.fox_food.set_fullness_value(2);
t.fox_food.set_on_use_effect('q.entity.heal(2)');
q.entity.behaviour.item_interact.add_pickup_item(t.fox_food);
q.append(q.entity.config.pickup_items, t.fox_food);
'Add regular food to the pickup behaviour';
t.regular_food = q.create_pickup_item('q.item.is_food()', 4);
t.regular_food.set_fullness_value(1);
t.regular_food.set_on_use_effect('q.entity.heal(1)');
q.entity.behaviour.item_interact.add_pickup_item(t.regular_food);
q.append(q.entity.config.pickup_items, t.regular_food);
'Add a generic item to the pickup behaviour';
'Add items in general to the pickup behaviour since foxes love picking up shit';
t.generic_item = q.create_pickup_item(true, 1);
t.generic_item.set_fullness_value(0);
q.entity.behaviour.item_interact.add_pickup_item(t.generic_item);
q.entity.behaviour.item_interact.add_pickup_item(t.generic_item);
q.append(q.entity.config.pickup_items, t.generic_item);
@@ -0,0 +1,10 @@
'This script adds all the pickup items from the Pokemon (the species file) to the config struct';
t.poke = q.entity;
t.index = 0;
t.pickup_items = q.array();
for_each(t.item, t.poke.behaviour.item_interact.pickup_items, {
t.pickup_items[t.index] = t.item;
t.index = t.index + 1;
});
t.poke.config.pickup_items = t.pickup_items;
Binary file not shown.
@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>local.com.bedrockk</groupId>
<artifactId>molang</artifactId>
<version>1.1.19</version>
</project>
+3 -3
View File
@@ -3,10 +3,10 @@
<groupId>local.com.bedrockk</groupId>
<artifactId>molang</artifactId>
<versioning>
<release>1.1.18</release>
<release>1.1.19</release>
<versions>
<version>1.1.18</version>
<version>1.1.19</version>
</versions>
<lastUpdated>20231949390005</lastUpdated>
<lastUpdated>20231949390006</lastUpdated>
</versioning>
</metadata>
+1 -1
View File
@@ -14,7 +14,7 @@ loom = "1.10-SNAPSHOT"
architectury-plugin = "3.4-SNAPSHOT"
# Common
molang = "1.1.18"
molang = "1.1.19"
graal = "22.3.0"
icu4j = "71.1"
jei-api = "19.21.2.313"