package ar.com.companeros.horror; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.ai.navigation.GroundPathNavigation; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.PathNavigationRegion; import net.minecraft.world.level.block.TrapDoorBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.pathfinder.*; import net.minecraft.world.phys.Vec3; /** Planifica para el cuerpo plegado, pero sólo avanza cuando la postura física ya cabe. */ final class ParasiteNavigation extends GroundPathNavigation { private BlockPos climbDestination; private double climbSpeed; ParasiteNavigation(ParasiteEntity mob, Level level) { super(mob, level); } @Override protected PathFinder createPathFinder(int maximumNodes) { nodeEvaluator = new CompactWalkEvaluator(); nodeEvaluator.setCanPassDoors(true); return new PathFinder(nodeEvaluator, maximumNodes); } @Override public void tick() { ParasiteEntity body = (ParasiteEntity) mob; Vec3 next = nextWaypoint(); if (next != null && body.needsClearancePause(next)) { body.getMoveControl().setWantedPosition(body.getX(), body.getY(), body.getZ(), 0); body.setSpeed(0); body.setDeltaMovement(0, body.getDeltaMovement().y, 0); return; } super.tick(); if (isDone() && canClimbTowardDestination()) { body.getMoveControl().setWantedPosition(climbDestination.getX() + .5, climbDestination.getY(), climbDestination.getZ() + .5, climbSpeed); } } @Override public boolean moveTo(Entity target, double speed) { boolean planned = super.moveTo(target, speed); climbDestination = target.blockPosition(); climbSpeed = speed; return planned || canClimbTowardDestination(); } @Override public void stop() { super.stop(); climbDestination = null; } private boolean canClimbTowardDestination() { ParasiteEntity body = (ParasiteEntity) mob; return climbDestination != null && body.getMutationStage() >= 2 && body.intent() != ParasiteEntity.Intent.RETREAT && (body.intent() == ParasiteEntity.Intent.CLIMB || climbDestination.getY() > body.getY() + .5); } Vec3 nextWaypoint() { return path == null || path.isDone() ? null : path.getNextEntityPos(mob); } private static final class CompactWalkEvaluator extends WalkNodeEvaluator { @Override public void prepare(PathNavigationRegion region, Mob body) { super.prepare(region, body); // La búsqueda puede proponer túneles de un bloque sin reducir antes el cuerpo real. entityHeight = 1; } @Override public BlockPathTypes getBlockPathType(BlockGetter world, int x, int y, int z) { BlockPos position = new BlockPos(x, y, z); BlockState state = world.getBlockState(position); if (state.getBlock() instanceof TrapDoorBlock) { if (!state.getValue(TrapDoorBlock.OPEN)) return BlockPathTypes.BLOCKED; Vec3 feet = new Vec3(x + .5, y, z + .5); if (!ParasiteClearance.fits(level, mob, feet, ParasiteClearance.COMPACT_HEIGHT)) return BlockPathTypes.BLOCKED; return world.getBlockState(position.below()).getCollisionShape(world, position.below()).isEmpty() ? BlockPathTypes.OPEN : BlockPathTypes.WALKABLE; } return super.getBlockPathType(world, x, y, z); } @Override protected boolean isNeighborValid(Node next, Node previous) { return super.isNeighborValid(next, previous) && compactEdgeClear(previous, next); } @Override protected boolean isDiagonalValid(Node previous, Node sideOne, Node sideTwo, Node next) { return super.isDiagonalValid(previous, sideOne, sideTwo, next) && compactEdgeClear(previous, next); } private boolean compactEdgeClear(Node from, Node to) { if (from == null || to == null) return false; Vec3 beginning = new Vec3(from.x + .5, getFloorLevel(new BlockPos(from.x, from.y, from.z)), from.z + .5); Vec3 end = new Vec3(to.x + .5, getFloorLevel(new BlockPos(to.x, to.y, to.z)), to.z + .5); // Los escalones requieren el salto vanilla; el barrido horizontal se evalúa a la cota superior. double top = Math.max(beginning.y, end.y); return ParasiteClearance.clearSegment(level, mob, new Vec3(beginning.x, top, beginning.z), new Vec3(end.x, top, end.z), ParasiteClearance.COMPACT_HEIGHT); } } }