diff --git a/common/src/main/kotlin/quaedam/Quaedam.kt b/common/src/main/kotlin/quaedam/Quaedam.kt index fc7cab7..ea6839d 100644 --- a/common/src/main/kotlin/quaedam/Quaedam.kt +++ b/common/src/main/kotlin/quaedam/Quaedam.kt @@ -26,6 +26,7 @@ object Quaedam { val blockEntities = DeferredRegister.create(ID, Registries.BLOCK_ENTITY_TYPE)!! val entities = DeferredRegister.create(ID, Registries.ENTITY_TYPE)!! 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 projectionEffects = DeferredRegister.create(ID, ProjectionEffectType.registryKey)!! @@ -47,6 +48,7 @@ object Quaedam { blockEntities.register() entities.register() schedules.register() + memoryTypes.register() sensors.register() projectionEffects.register() } diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ai/BedInChunkSensor.kt b/common/src/main/kotlin/quaedam/projection/swarm/ai/BedInChunkSensor.kt index 992b594..64f1db7 100644 --- a/common/src/main/kotlin/quaedam/projection/swarm/ai/BedInChunkSensor.kt +++ b/common/src/main/kotlin/quaedam/projection/swarm/ai/BedInChunkSensor.kt @@ -2,7 +2,7 @@ package quaedam.projection.swarm.ai import net.minecraft.core.GlobalPos 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.MemoryStatus 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 quaedam.Quaedam -class BedInChunkSensor : Sensor() { +class BedInChunkSensor : Sensor() { companion object { - val sensor = Quaedam.sensors.register("bed_in_chunk") { + const val ID = "bed_in_chunk" + + val sensor = Quaedam.sensors.register(ID) { SensorType(::BedInChunkSensor) } @@ -24,7 +26,7 @@ class BedInChunkSensor : Sensor() { 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 level.getChunkAt(entity.blockPosition()).blockEntities .filterValues { it is BedBlockEntity } diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ai/ExchangeItem.kt b/common/src/main/kotlin/quaedam/projection/swarm/ai/ExchangeItem.kt new file mode 100644 index 0000000..b7e96d3 --- /dev/null +++ b/common/src/main/kotlin/quaedam/projection/swarm/ai/ExchangeItem.kt @@ -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 : Behavior( + 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) + } + } + } + } + } + +} diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ai/NearestVisibleContainer.kt b/common/src/main/kotlin/quaedam/projection/swarm/ai/NearestVisibleContainer.kt new file mode 100644 index 0000000..eb0e6c3 --- /dev/null +++ b/common/src/main/kotlin/quaedam/projection/swarm/ai/NearestVisibleContainer.kt @@ -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() { + + 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) + } + } + +} \ No newline at end of file diff --git a/common/src/main/kotlin/quaedam/projection/swarm/ai/ProjectedPersonAI.kt b/common/src/main/kotlin/quaedam/projection/swarm/ai/ProjectedPersonAI.kt index 8462385..10d870a 100644 --- a/common/src/main/kotlin/quaedam/projection/swarm/ai/ProjectedPersonAI.kt +++ b/common/src/main/kotlin/quaedam/projection/swarm/ai/ProjectedPersonAI.kt @@ -52,6 +52,7 @@ object ProjectedPersonAI { init { BedInChunkSensor + NearestVisibleContainer } private val memoryTypes by lazy { @@ -67,6 +68,7 @@ object ProjectedPersonAI { MemoryModuleType.CANT_REACH_WALK_TARGET_SINCE, MemoryModuleType.HOME, MemoryModuleType.LAST_WOKEN, + NearestVisibleContainer.memory.get(), ) } @@ -77,6 +79,7 @@ object ProjectedPersonAI { SensorType.HURT_BY, SensorType.NEAREST_ITEMS, BedInChunkSensor.sensor.get(), + NearestVisibleContainer.sensor.get(), ) } @@ -146,6 +149,7 @@ object ProjectedPersonAI { private fun initWorkActivity(brain: Brain) { brain.addActivity( Activity.WORK, ImmutableList.of( + 7 weight ExchangeItem(), 10 weight createStrollBehavior(), 99 weight UpdateActivityFromSchedule.create(), ) diff --git a/common/src/main/resources/quaedam.accesswidener b/common/src/main/resources/quaedam.accesswidener index e7940ef..37b50d0 100644 --- a/common/src/main/resources/quaedam.accesswidener +++ b/common/src/main/resources/quaedam.accesswidener @@ -4,3 +4,5 @@ accessible method net/minecraft/core/registries/BuiltInRegistries registerSimple accessible class net/minecraft/core/registries/BuiltInRegistries$RegistryBootstrap # Entity Brain AI accessible method net/minecraft/world/entity/ai/sensing/SensorType (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