feat: item exchange

This commit is contained in:
xtex 2023-07-03 11:51:39 +08:00
parent f2ceaf176c
commit e13d5ff178
Signed by: xtex
GPG Key ID: B918086ED8045B91
6 changed files with 181 additions and 4 deletions

View File

@ -26,6 +26,7 @@ object Quaedam {
val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!! val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!!
val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!! val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!!
val schedules = DeferredRegister.create(ID, Registries.SCHEDULE)!! val schedules = DeferredRegister.create(ID, Registries.SCHEDULE)!!
val memoryTypes = DeferredRegister.create(ID, Registries.MEMORY_MODULE_TYPE)!!
val sensors = DeferredRegister.create(ID, Registries.SENSOR_TYPE)!! val sensors = DeferredRegister.create(ID, Registries.SENSOR_TYPE)!!
val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!! val projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!!
@ -47,6 +48,7 @@ object Quaedam {
blockEntities.register() blockEntities.register()
entities.register() entities.register()
schedules.register() schedules.register()
memoryTypes.register()
sensors.register() sensors.register()
projectionEffects.register() projectionEffects.register()
} }

View File

@ -2,7 +2,7 @@ package quaedam.projection.swarm.ai
import net.minecraft.core.GlobalPos import net.minecraft.core.GlobalPos
import net.minecraft.server.level.ServerLevel import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.Mob import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.sensing.Sensor import net.minecraft.world.entity.ai.sensing.Sensor
@ -12,11 +12,13 @@ import net.minecraft.world.level.block.entity.BedBlockEntity
import net.minecraft.world.level.block.state.properties.BedPart import net.minecraft.world.level.block.state.properties.BedPart
import quaedam.Quaedam import quaedam.Quaedam
class BedInChunkSensor : Sensor<Mob>() { class BedInChunkSensor : Sensor<LivingEntity>() {
companion object { companion object {
val sensor = Quaedam.sensors.register("bed_in_chunk") { const val ID = "bed_in_chunk"
val sensor = Quaedam.sensors.register(ID) {
SensorType(::BedInChunkSensor) SensorType(::BedInChunkSensor)
} }
@ -24,7 +26,7 @@ class BedInChunkSensor : Sensor<Mob>() {
override fun requires() = setOf(MemoryModuleType.NEAREST_BED) override fun requires() = setOf(MemoryModuleType.NEAREST_BED)
override fun doTick(level: ServerLevel, entity: Mob) { override fun doTick(level: ServerLevel, entity: LivingEntity) {
if (entity.tickCount and 0b11111 == 0 && !entity.isSleeping) { // 32gt if (entity.tickCount and 0b11111 == 0 && !entity.isSleeping) { // 32gt
level.getChunkAt(entity.blockPosition()).blockEntities level.getChunkAt(entity.blockPosition()).blockEntities
.filterValues { it is BedBlockEntity } .filterValues { it is BedBlockEntity }

View File

@ -0,0 +1,126 @@
package quaedam.projection.swarm.ai
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.sounds.SoundEvents
import net.minecraft.world.Container
import net.minecraft.world.entity.Mob
import net.minecraft.world.entity.ai.behavior.Behavior
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.memory.MemoryStatus
import net.minecraft.world.entity.ai.memory.WalkTarget
import net.minecraft.world.entity.npc.InventoryCarrier
import net.minecraft.world.item.ItemStack
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
import net.minecraft.world.level.block.entity.ChestBlockEntity
import kotlin.math.min
class ExchangeItem<E> : Behavior<E>(
mapOf(
MemoryModuleType.WALK_TARGET to MemoryStatus.VALUE_ABSENT,
NearestVisibleContainer.memory.get() to MemoryStatus.VALUE_PRESENT,
), 5 * 20, 12 * 20
) where E : Mob, E : InventoryCarrier {
private var target: BlockPos? = null
private var closeAt: Long? = null
override fun start(level: ServerLevel, entity: E, l: Long) {
target = entity.brain.getMemory(NearestVisibleContainer.memory.get()).get()
closeAt = null
entity.brain.setMemory(MemoryModuleType.WALK_TARGET, WalkTarget(target!!, 1.0f, 2))
}
override fun canStillUse(level: ServerLevel, entity: E, l: Long) =
entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isPresent || entity.brain.getMemory(MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE).isEmpty || (closeAt != null && closeAt!! < l)
override fun tick(level: ServerLevel, entity: E, l: Long) {
if (closeAt == null) {
if (entity.brain.getMemory(MemoryModuleType.WALK_TARGET).isEmpty) {
// reached
val chest = level.getBlockEntity(target!!) as BaseContainerBlockEntity
if (chest is ChestBlockEntity) {
ChestBlockEntity.playSound(level, target!!, level.getBlockState(target!!), SoundEvents.CHEST_OPEN)
}
if (chest.isEmpty) {
closeAt = l + 7
} else {
closeAt = l + 10 + level.random.nextInt(100)
exchangeItems(level, entity)
}
}
}
}
override fun stop(level: ServerLevel, entity: E, l: Long) {
entity.brain.eraseMemory(MemoryModuleType.WALK_TARGET)
if (closeAt != null) {
// opened
val chest = level.getBlockEntity(target!!)!!
if (chest is ChestBlockEntity) {
ChestBlockEntity.playSound(level, target!!, level.getBlockState(target!!), SoundEvents.CHEST_CLOSE)
}
}
}
private fun exchangeItems(level: ServerLevel, entity: E) {
val container = level.getBlockEntity(target!!) as Container
val inventory = entity.inventory
for (i in 1..6) {
val maxCount = level.random.nextInt(16)
if (level.random.nextBoolean()) {
// take
val slot = level.random.nextInt(container.containerSize)
val item = container.getItem(slot)
if (!item.isEmpty) {
val takeCount = min(item.count, maxCount)
val takeItem = item.copyWithCount(takeCount)
if (inventory.canTakeItem(container, slot, takeItem) && entity.canHoldItem(takeItem)) {
val remaining = entity.inventory.addItem(entity.equipItemIfPossible(takeItem))
val actualCount = takeCount - remaining.count
item.shrink(actualCount)
container.setItem(slot, item)
}
}
} else {
// put
val slot = level.random.nextInt(inventory.containerSize)
val item = inventory.getItem(slot)
if (!item.isEmpty) {
val takeCount = min(item.count, maxCount)
val takeItem = item.copyWithCount(takeCount)
if (container.canTakeItem(inventory, slot, takeItem)) {
for (target in 0 until container.containerSize) {
val targetItem = container.getItem(target)
if (ItemStack.isSameItemSameTags(targetItem, takeItem)) {
val resultCount = min(targetItem.count + takeItem.count, item.maxStackSize)
val putCount = resultCount - targetItem.count
if (putCount != 0) {
targetItem.grow(putCount)
container.setItem(target, targetItem)
takeItem.shrink(putCount)
if (takeItem.isEmpty) break
}
}
}
if (!takeItem.isEmpty) {
for (target in 0 until container.containerSize) {
val targetItem = container.getItem(target)
if (targetItem.isEmpty) {
container.setItem(target, takeItem.copyAndClear())
println("put all at $target")
break
}
}
}
val putCount = takeCount - takeItem.count
println("put $putCount")
item.shrink(putCount)
container.setItem(slot, item)
}
}
}
}
}
}

View File

@ -0,0 +1,41 @@
package quaedam.projection.swarm.ai
import net.minecraft.core.BlockPos
import net.minecraft.server.level.ServerLevel
import net.minecraft.world.entity.LivingEntity
import net.minecraft.world.entity.ai.memory.MemoryModuleType
import net.minecraft.world.entity.ai.sensing.Sensor
import net.minecraft.world.entity.ai.sensing.SensorType
import net.minecraft.world.level.block.entity.BaseContainerBlockEntity
import quaedam.Quaedam
import quaedam.utils.getChunksNearby
import java.util.*
class NearestVisibleContainer : Sensor<LivingEntity>() {
companion object {
const val ID = "nearest_visible_container"
val sensor = Quaedam.sensors.register(ID) {
SensorType(::NearestVisibleContainer)
}!!
val memory = Quaedam.memoryTypes.register(ID) {
MemoryModuleType(Optional.of(BlockPos.CODEC))
}!!
}
override fun requires() = setOf(memory.get())
override fun doTick(level: ServerLevel, entity: LivingEntity) {
if (entity.tickCount and 0b11111 == 0) { // 32gt
val pos = level.getChunksNearby(entity.blockPosition(), 1)
.flatMap { it.blockEntities.filterValues { be -> be is BaseContainerBlockEntity }.keys }
.minByOrNull { it.distManhattan(entity.blockPosition()) }
entity.brain.setMemory(memory.get(), pos)
}
}
}

View File

@ -52,6 +52,7 @@ object ProjectedPersonAI {
init { init {
BedInChunkSensor BedInChunkSensor
NearestVisibleContainer
} }
private val memoryTypes by lazy { private val memoryTypes by lazy {
@ -67,6 +68,7 @@ object ProjectedPersonAI {
MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE,
MemoryModuleType.HOME, MemoryModuleType.HOME,
MemoryModuleType.LAST_WOKEN, MemoryModuleType.LAST_WOKEN,
NearestVisibleContainer.memory.get(),
) )
} }
@ -77,6 +79,7 @@ object ProjectedPersonAI {
SensorType.HURT_BY, SensorType.HURT_BY,
SensorType.NEAREST_ITEMS, SensorType.NEAREST_ITEMS,
BedInChunkSensor.sensor.get(), BedInChunkSensor.sensor.get(),
NearestVisibleContainer.sensor.get(),
) )
} }
@ -146,6 +149,7 @@ object ProjectedPersonAI {
private fun initWorkActivity(brain: Brain<ProjectedPersonEntity>) { private fun initWorkActivity(brain: Brain<ProjectedPersonEntity>) {
brain.addActivity( brain.addActivity(
Activity.WORK, ImmutableList.of( Activity.WORK, ImmutableList.of(
7 weight ExchangeItem(),
10 weight createStrollBehavior(), 10 weight createStrollBehavior(),
99 weight UpdateActivityFromSchedule.create(), 99 weight UpdateActivityFromSchedule.create(),
) )

View File

@ -4,3 +4,5 @@ accessible method net/minecraft/core/registries/BuiltInRegistries registerSimple
accessible class net/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap accessible class net/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap
# Entity Brain AI # Entity Brain AI
accessible method net/minecraft/world/entity/ai/sensing/SensorType <init> (Ljava/util/function/Supplier;)V accessible method net/minecraft/world/entity/ai/sensing/SensorType <init> (Ljava/util/function/Supplier;)V
# ExchangeItem behaviour
accessible method net/minecraft/world/level/block/entity/ChestBlockEntity playSound (Lnet/minecraft/world/level/Level;Lnet/minecraft/core/BlockPos;Lnet/minecraft/world/level/block/state/BlockState;Lnet/minecraft/sounds/SoundEvent;)V