package net.minecraft.world.entity.ai.behavior; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Lists; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.ItemTags; import net.minecraft.world.SimpleContainer; 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.Villager; import net.minecraft.world.entity.npc.VillagerProfession; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.CropBlock; import net.minecraft.world.level.block.FarmBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEvent.Context; import org.jetbrains.annotations.Nullable; public class HarvestFarmland extends Behavior { private static final int HARVEST_DURATION = 200; public static final float SPEED_MODIFIER = 0.5F; @Nullable private BlockPos aboveFarmlandPos; private long nextOkStartTime; private int timeWorkedSoFar; private final List validFarmlandAroundVillager = Lists.newArrayList(); public HarvestFarmland() { super( ImmutableMap.of( MemoryModuleType.LOOK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.WALK_TARGET, MemoryStatus.VALUE_ABSENT, MemoryModuleType.SECONDARY_JOB_SITE, MemoryStatus.VALUE_PRESENT ) ); } protected boolean checkExtraStartConditions(ServerLevel level, Villager owner) { if (!level.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING)) { return false; } else if (!owner.getVillagerData().profession().is(VillagerProfession.FARMER)) { return false; } else { BlockPos.MutableBlockPos mutableBlockPos = owner.blockPosition().mutable(); this.validFarmlandAroundVillager.clear(); for (int i = -1; i <= 1; i++) { for (int j = -1; j <= 1; j++) { for (int k = -1; k <= 1; k++) { mutableBlockPos.set(owner.getX() + i, owner.getY() + j, owner.getZ() + k); if (this.validPos(mutableBlockPos, level)) { this.validFarmlandAroundVillager.add(new BlockPos(mutableBlockPos)); } } } } this.aboveFarmlandPos = this.getValidFarmland(level); return this.aboveFarmlandPos != null; } } @Nullable private BlockPos getValidFarmland(ServerLevel serverLevel) { return this.validFarmlandAroundVillager.isEmpty() ? null : (BlockPos)this.validFarmlandAroundVillager.get(serverLevel.getRandom().nextInt(this.validFarmlandAroundVillager.size())); } private boolean validPos(BlockPos pos, ServerLevel serverLevel) { BlockState blockState = serverLevel.getBlockState(pos); Block block = blockState.getBlock(); Block block2 = serverLevel.getBlockState(pos.below()).getBlock(); return block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockState) || blockState.isAir() && block2 instanceof FarmBlock; } protected void start(ServerLevel level, Villager entity, long gameTime) { if (gameTime > this.nextOkStartTime && this.aboveFarmlandPos != null) { entity.getBrain().setMemory(MemoryModuleType.LOOK_TARGET, new BlockPosTracker(this.aboveFarmlandPos)); entity.getBrain().setMemory(MemoryModuleType.WALK_TARGET, new WalkTarget(new BlockPosTracker(this.aboveFarmlandPos), 0.5F, 1)); } } protected void stop(ServerLevel level, Villager entity, long gameTime) { entity.getBrain().eraseMemory(MemoryModuleType.LOOK_TARGET); entity.getBrain().eraseMemory(MemoryModuleType.WALK_TARGET); this.timeWorkedSoFar = 0; this.nextOkStartTime = gameTime + 40L; } protected void tick(ServerLevel level, Villager owner, long gameTime) { if (this.aboveFarmlandPos == null || this.aboveFarmlandPos.closerToCenterThan(owner.position(), 1.0)) { if (this.aboveFarmlandPos != null && gameTime > this.nextOkStartTime) { BlockState blockState = level.getBlockState(this.aboveFarmlandPos); Block block = blockState.getBlock(); Block block2 = level.getBlockState(this.aboveFarmlandPos.below()).getBlock(); if (block instanceof CropBlock && ((CropBlock)block).isMaxAge(blockState)) { level.destroyBlock(this.aboveFarmlandPos, true, owner); } if (blockState.isAir() && block2 instanceof FarmBlock && owner.hasFarmSeeds()) { SimpleContainer simpleContainer = owner.getInventory(); for (int i = 0; i < simpleContainer.getContainerSize(); i++) { ItemStack itemStack = simpleContainer.getItem(i); boolean bl = false; if (!itemStack.isEmpty() && itemStack.is(ItemTags.VILLAGER_PLANTABLE_SEEDS) && itemStack.getItem() instanceof BlockItem blockItem) { BlockState blockState2 = blockItem.getBlock().defaultBlockState(); level.setBlockAndUpdate(this.aboveFarmlandPos, blockState2); level.gameEvent(GameEvent.BLOCK_PLACE, this.aboveFarmlandPos, Context.of(owner, blockState2)); bl = true; } if (bl) { level.playSound( null, this.aboveFarmlandPos.getX(), this.aboveFarmlandPos.getY(), this.aboveFarmlandPos.getZ(), SoundEvents.CROP_PLANTED, SoundSource.BLOCKS, 1.0F, 1.0F ); itemStack.shrink(1); if (itemStack.isEmpty()) { simpleContainer.setItem(i, ItemStack.EMPTY); } break; } } } if (block instanceof CropBlock && !((CropBlock)block).isMaxAge(blockState)) { this.validFarmlandAroundVillager.remove(this.aboveFarmlandPos); this.aboveFarmlandPos = this.getValidFarmland(level); if (this.aboveFarmlandPos != null) { this.nextOkStartTime = gameTime + 20L; owner.getBrain().setMemory(MemoryModuleType.WALK_TARGET, new WalkTarget(new BlockPosTracker(this.aboveFarmlandPos), 0.5F, 1)); owner.getBrain().setMemory(MemoryModuleType.LOOK_TARGET, new BlockPosTracker(this.aboveFarmlandPos)); } } } this.timeWorkedSoFar++; } } protected boolean canStillUse(ServerLevel level, Villager entity, long gameTime) { return this.timeWorkedSoFar < 200; } }