Volver al índice
src/main/java/ar/com/companeros/horror/ParasiteNavigation.java
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);
}
}
}