package com.kasetoatz.hungryfrog.task; import com.google.common.collect.ImmutableMap; import com.kasetoatz.hungryfrog.HungryFrog; import net.minecraft.block.*; import net.minecraft.command.argument.EntityAnchorArgumentType; import net.minecraft.entity.EntityPose; import net.minecraft.entity.ItemEntity; import net.minecraft.entity.ai.brain.MemoryModuleState; import net.minecraft.entity.ai.brain.MemoryModuleType; import net.minecraft.entity.ai.brain.WalkTarget; import net.minecraft.entity.ai.brain.task.MultiTickTask; import net.minecraft.entity.ai.pathing.Path; import net.minecraft.entity.passive.FrogEntity; import net.minecraft.fluid.FluidState; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.registry.tag.FluidTags; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; import net.minecraft.sound.SoundEvents; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; import java.util.ArrayList; import java.util.List; import java.util.Optional; public class FrogEatBlockTask extends MultiTickTask { public static final SoundEvent TONGUE_SOUND = SoundEvents.ENTITY_FROG_TONGUE; public static final SoundEvent EAT_SOUND = SoundEvents.ENTITY_FROG_EAT; private int eatTick; private int moveToTargetTick; private Phase phase; public FrogEatBlockTask() { super(ImmutableMap.of(HungryFrog.BLOCK_TO_EAT, MemoryModuleState.VALUE_PRESENT), 100); this.phase = Phase.DONE; } protected boolean shouldRun(ServerWorld world, FrogEntity frog) { Optional memory = frog.getBrain().getOptionalRegisteredMemory(HungryFrog.BLOCK_TO_EAT); if (memory.isEmpty()) { return false; } BlockPos pos = memory.get(); boolean reachable = this.isBlockReachable(frog, pos); if (!reachable) { frog.getBrain().forget(HungryFrog.BLOCK_TO_EAT); this.markTargetAsUnreachable(frog, pos); } return reachable && frog.getPose() != EntityPose.CROAKING && frog.getBrain().getOptionalRegisteredMemory(MemoryModuleType.ATTACK_TARGET).isEmpty(); } protected boolean shouldKeepRunning(ServerWorld world, FrogEntity frog, long time) { return frog.getBrain().hasMemoryModule(HungryFrog.BLOCK_TO_EAT) && this.phase != Phase.DONE && !frog.getBrain().hasMemoryModule(MemoryModuleType.IS_PANICKING); } protected void run(ServerWorld world, FrogEntity frog, long time) { Optional value = frog.getBrain().getOptionalRegisteredMemory(HungryFrog.BLOCK_TO_EAT); if (value.isEmpty()) { return; } BlockPos pos = value.get(); frog.lookAt(EntityAnchorArgumentType.EntityAnchor.EYES, Vec3d.of(pos)); frog.getBrain().remember(MemoryModuleType.WALK_TARGET, new WalkTarget(pos, 2.F, 0)); this.moveToTargetTick = 10; this.phase = Phase.MOVE_TO_TARGET; } protected void finishRunning(ServerWorld world, FrogEntity frog, long time) { frog.getBrain().forget(HungryFrog.BLOCK_TO_EAT); frog.setPose(EntityPose.STANDING); } private void absorb(ServerWorld world, BlockPos pos) { BlockPos.iterateRecursively(pos, 6, 65, (currentPos, queuer) -> { for (Direction direction : Direction.values()) { queuer.accept(currentPos.offset(direction)); } }, currentPos -> { if (currentPos.equals(pos)) { return BlockPos.IterationState.ACCEPT; } else { BlockState blockState = world.getBlockState(currentPos); FluidState fluidState = world.getFluidState(currentPos); if (!fluidState.isIn(FluidTags.WATER) && !fluidState.isIn(FluidTags.LAVA)) { return BlockPos.IterationState.SKIP; } else if (blockState.getBlock() instanceof FluidDrainable fluidDrainable && !fluidDrainable.tryDrainFluid(null, world, currentPos, blockState).isEmpty()) { return BlockPos.IterationState.ACCEPT; } else { if (blockState.getBlock() instanceof FluidBlock) { world.setBlockState(currentPos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL); } else { if (!blockState.isOf(Blocks.KELP) && !blockState.isOf(Blocks.KELP_PLANT) && !blockState.isOf(Blocks.SEAGRASS) && !blockState.isOf(Blocks.TALL_SEAGRASS)) { return BlockPos.IterationState.SKIP; } world.setBlockState(currentPos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL); } return BlockPos.IterationState.ACCEPT; } } }); } private void eat(ServerWorld world, FrogEntity frog, BlockPos pos) { world.playSoundFromEntity(null, frog, EAT_SOUND, SoundCategory.NEUTRAL, 2.0F, 1.0F); BlockState state = world.getBlockState(pos); Item item = Item.BLOCK_ITEMS.get(state.getBlock()); if (state.isOf(Blocks.WATER) || state.isOf(Blocks.LAVA)) { world.setBlockState(pos, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL); this.absorb(world, pos); } else { world.removeBlock(pos, false); } if (item == null) { return; } ItemStack stack = new ItemStack(item); ItemEntity entity = new ItemEntity(world, frog.getX(), frog.getY(), frog.getZ(), stack); world.spawnEntity(entity); } protected void keepRunning(ServerWorld world, FrogEntity frog, long time) { Optional memory = frog.getBrain().getOptionalRegisteredMemory(HungryFrog.BLOCK_TO_EAT); if (memory.isEmpty()) { return; } BlockPos pos = memory.get(); switch (this.phase) { case MOVE_TO_TARGET: if (frog.getPos().distanceTo(Vec3d.of(pos)) < 1.75F) { world.playSoundFromEntity(null, frog, TONGUE_SOUND, SoundCategory.NEUTRAL, 2.0F, 1.0F); frog.setPose(EntityPose.USING_TONGUE); this.eatTick = 0; this.phase = Phase.CATCH_ANIMATION; } else if (this.moveToTargetTick <= 0) { frog.getBrain().remember(MemoryModuleType.WALK_TARGET, new WalkTarget(pos, 2.F, 0)); this.moveToTargetTick = 10; } else { this.moveToTargetTick--; } break; case CATCH_ANIMATION: if (this.eatTick++ >= 6) { this.phase = Phase.EAT_ANIMATION; this.eat(world, frog, pos); } break; case EAT_ANIMATION: if (this.eatTick >= 10) { this.phase = Phase.DONE; } else { this.eatTick++; } } } private boolean isBlockReachable(FrogEntity frog, BlockPos pos) { Path path = frog.getNavigation().findPathTo(pos, 0); return path != null && path.getManhattanDistanceFromTarget() < 1.75F; } private void markTargetAsUnreachable(FrogEntity frog, BlockPos pos) { try { List list = frog.getBrain().getOptionalRegisteredMemory(HungryFrog.UNREACHABLE_BLOCK_TARGETS).orElse(new ArrayList<>()); boolean contains = !list.contains(pos); if (list.size() == 5 && contains) { list.removeFirst(); } if (contains) { list.add(pos); } frog.getBrain().remember(HungryFrog.UNREACHABLE_BLOCK_TARGETS, list, 100L); } catch (Exception e) { frog.getBrain().forget(HungryFrog.UNREACHABLE_BLOCK_TARGETS); } } private enum Phase { MOVE_TO_TARGET, CATCH_ANIMATION, EAT_ANIMATION, DONE } }