package net.minecraft.world.entity.ai.behavior; import java.util.Optional; import java.util.function.Function; import java.util.function.Predicate; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.PathfinderMob; import net.minecraft.world.entity.ai.behavior.declarative.BehaviorBuilder; import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.ai.memory.WalkTarget; import net.minecraft.world.entity.ai.util.AirAndWaterRandomPos; import net.minecraft.world.entity.ai.util.LandRandomPos; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class RandomStroll { private static final int MAX_XZ_DIST = 10; private static final int MAX_Y_DIST = 7; private static final int[][] SWIM_XY_DISTANCE_TIERS = new int[][]{{1, 1}, {3, 3}, {5, 5}, {6, 5}, {7, 7}, {10, 7}}; public static OneShot stroll(float speedModifier) { return stroll(speedModifier, true); } public static OneShot stroll(float speedModifier, boolean mayStrollFromWater) { return strollFlyOrSwim( speedModifier, pathfinderMob -> LandRandomPos.getPos(pathfinderMob, 10, 7), mayStrollFromWater ? pathfinderMob -> true : pathfinderMob -> !pathfinderMob.isInWater() ); } public static BehaviorControl stroll(float speedModifier, int maxHorizontalDistance, int maxVerticalDistance) { return strollFlyOrSwim(speedModifier, pathfinderMob -> LandRandomPos.getPos(pathfinderMob, maxHorizontalDistance, maxVerticalDistance), pathfinderMob -> true); } public static BehaviorControl fly(float speedModifier) { return strollFlyOrSwim(speedModifier, pathfinderMob -> getTargetFlyPos(pathfinderMob, 10, 7), pathfinderMob -> true); } public static BehaviorControl swim(float speedModifier) { return strollFlyOrSwim(speedModifier, RandomStroll::getTargetSwimPos, Entity::isInWater); } private static OneShot strollFlyOrSwim(float speedModifier, Function target, Predicate canStroll) { return BehaviorBuilder.create( instance -> instance.group(instance.absent(MemoryModuleType.WALK_TARGET)).apply(instance, memoryAccessor -> (serverLevel, pathfinderMob, l) -> { if (!canStroll.test(pathfinderMob)) { return false; } else { Optional optional = Optional.ofNullable((Vec3)target.apply(pathfinderMob)); memoryAccessor.setOrErase(optional.map(vec3 -> new WalkTarget(vec3, speedModifier, 0))); return true; } }) ); } @Nullable private static Vec3 getTargetSwimPos(PathfinderMob mob) { Vec3 vec3 = null; Vec3 vec32 = null; for (int[] is : SWIM_XY_DISTANCE_TIERS) { if (vec3 == null) { vec32 = BehaviorUtils.getRandomSwimmablePos(mob, is[0], is[1]); } else { vec32 = mob.position().add(mob.position().vectorTo(vec3).normalize().multiply(is[0], is[1], is[0])); } if (vec32 == null || mob.level().getFluidState(BlockPos.containing(vec32)).isEmpty()) { return vec3; } vec3 = vec32; } return vec32; } @Nullable private static Vec3 getTargetFlyPos(PathfinderMob mob, int maxDistance, int yRange) { Vec3 vec3 = mob.getViewVector(0.0F); return AirAndWaterRandomPos.getPos(mob, maxDistance, yRange, -2, vec3.x, vec3.z, (float) (Math.PI / 2)); } }