Fix door stuck nudge, double door pairing/priority, and shelter findShelter Y-scan

This commit is contained in:
2026-06-18 17:33:20 +03:00
parent e6f0c841fe
commit da7e65d32e
2 changed files with 88 additions and 45 deletions
@@ -6,6 +6,7 @@ import net.minecraft.core.Direction;
import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.level.block.DoorBlock; import net.minecraft.world.level.block.DoorBlock;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.DoorHingeSide;
import net.minecraft.world.level.pathfinder.Node; import net.minecraft.world.level.pathfinder.Node;
import net.minecraft.world.level.pathfinder.Path; import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@@ -15,7 +16,7 @@ import java.util.EnumSet;
public class EllieDoorInteractGoal extends Goal { public class EllieDoorInteractGoal extends Goal {
private static final int CLOSE_DELAY = 25; private static final int CLOSE_DELAY = 25;
private static final double CLOSE_DIST_SQ = 2.0 * 2.0; private static final double CLOSE_DIST_SQ = 2.0 * 2.0;
private static final double STUCK_DELTA = 0.005; private static final int STUCK_TICK_LIMIT = 40;
private final EllieEntity ellie; private final EllieEntity ellie;
private BlockPos doorPos; private BlockPos doorPos;
@@ -24,7 +25,7 @@ public class EllieDoorInteractGoal extends Goal {
private int closeTimer; private int closeTimer;
private Vec3 doorStuckPos; private Vec3 doorStuckPos;
private int doorStuckTicks; private int doorStuckTicks;
private boolean stuckNudgeCw; private boolean lastNudgeWasCw;
public EllieDoorInteractGoal(EllieEntity ellie) { public EllieDoorInteractGoal(EllieEntity ellie) {
this.ellie = ellie; this.ellie = ellie;
@@ -57,7 +58,7 @@ public class EllieDoorInteractGoal extends Goal {
hasPassed = true; hasPassed = true;
closeTimer = CLOSE_DELAY; closeTimer = CLOSE_DELAY;
} else { } else {
checkStuck(); checkDoorStuck();
} }
} else if (--closeTimer <= 0) { } else if (--closeTimer <= 0) {
closeDoor(doorPos); closeDoor(doorPos);
@@ -87,40 +88,39 @@ public class EllieDoorInteractGoal extends Goal {
hasPassed = false; hasPassed = false;
closeTimer = 0; closeTimer = 0;
doorStuckPos = null;
doorStuckTicks = 0;
} }
} }
private void checkStuck() { private void checkDoorStuck() {
Vec3 current = ellie.position(); Vec3 current = ellie.position();
if (doorStuckPos != null) { if (doorStuckPos != null && current.distanceToSqr(doorStuckPos) < 0.005) {
if (current.distanceToSqr(doorStuckPos) < STUCK_DELTA) {
doorStuckTicks++; doorStuckTicks++;
if (doorStuckTicks > 40) { if (doorStuckTicks > STUCK_TICK_LIMIT) {
nudgeDoor(); nudgeFromDoor();
doorStuckTicks = 0; doorStuckTicks = 0;
} }
} else { } else {
doorStuckTicks = 0; doorStuckTicks = 0;
} }
}
doorStuckPos = current; doorStuckPos = current;
} }
private void nudgeDoor() { private void nudgeFromDoor() {
BlockState state = ellie.level().getBlockState(doorPos); BlockState state = ellie.level().getBlockState(doorPos);
if (!(state.getBlock() instanceof DoorBlock)) return; if (!(state.getBlock() instanceof DoorBlock)) return;
Direction facing = state.getValue(DoorBlock.FACING); Direction facing = state.getValue(DoorBlock.FACING);
Direction nudgeDir = stuckNudgeCw Direction nudgeDir = lastNudgeWasCw ? facing.getCounterClockWise() : facing.getClockWise();
? facing.getClockWise() lastNudgeWasCw = !lastNudgeWasCw;
: facing.getCounterClockWise();
stuckNudgeCw = !stuckNudgeCw;
ellie.setPos( double nx = nudgeDir.getStepX() * 0.125;
ellie.getX() + nudgeDir.getStepX() * 0.2, double nz = nudgeDir.getStepZ() * 0.125;
ellie.getY(), ellie.setPos(ellie.getX() + nx, ellie.getY(), ellie.getZ() + nz);
ellie.getZ() + nudgeDir.getStepZ() * 0.2
); Vec3 ahead = Vec3.atBottomCenterOf(doorPos.relative(facing));
ellie.getNavigation().moveTo(ahead.x, ahead.y, ahead.z, 0.6);
} }
private void openDoor(BlockPos pos, BlockState state) { private void openDoor(BlockPos pos, BlockState state) {
@@ -149,15 +149,15 @@ public class EllieDoorInteractGoal extends Goal {
if (door != null) return door; if (door != null) return door;
} }
} }
BlockPos center = ellie.blockPosition(); BlockPos center = ellie.blockPosition();
BlockPos singleDoor = null; BlockPos singleDoor = null;
for (int dx = -2; dx <= 2; dx++) { for (int dx = -2; dx <= 2; dx++) {
for (int dz = -2; dz <= 2; dz++) { for (int dz = -2; dz <= 2; dz++) {
for (int dy = -1; dy <= 1; dy++) { for (int dy = -1; dy <= 1; dy++) {
BlockPos checkPos = center.offset(dx, dy, dz); BlockPos checkPos = center.offset(dx, dy, dz);
if (!checkForDoor(checkPos)) continue; if (checkForDoor(checkPos)) {
BlockState state = ellie.level().getBlockState(checkPos); if (isDoubleDoor(checkPos)) {
if (findPairedDoor(checkPos, state) != null) {
return checkPos; return checkPos;
} }
if (singleDoor == null) { if (singleDoor == null) {
@@ -166,9 +166,15 @@ public class EllieDoorInteractGoal extends Goal {
} }
} }
} }
}
return singleDoor; return singleDoor;
} }
private boolean isDoubleDoor(BlockPos pos) {
BlockState state = ellie.level().getBlockState(pos);
return findPairedDoor(pos, state) != null;
}
private BlockPos resolveDoor(BlockPos pos) { private BlockPos resolveDoor(BlockPos pos) {
if (checkForDoor(pos)) return pos; if (checkForDoor(pos)) return pos;
if (checkForDoor(pos.above())) return pos.above(); if (checkForDoor(pos.above())) return pos.above();
@@ -188,12 +194,13 @@ public class EllieDoorInteractGoal extends Goal {
private BlockPos findPairedDoor(BlockPos pos, BlockState state) { private BlockPos findPairedDoor(BlockPos pos, BlockState state) {
Direction facing = state.getValue(DoorBlock.FACING); Direction facing = state.getValue(DoorBlock.FACING);
DoorHingeSide hinge = state.getValue(DoorBlock.HINGE);
for (Direction adj : Direction.Plane.HORIZONTAL) { for (Direction adj : Direction.Plane.HORIZONTAL) {
BlockPos adjPos = pos.relative(adj); BlockPos adjPos = pos.relative(adj);
BlockState adjState = ellie.level().getBlockState(adjPos); BlockState adjState = ellie.level().getBlockState(adjPos);
if (adjState.getBlock() instanceof DoorBlock) { if (adjState.getBlock() instanceof DoorBlock) {
Direction adjFacing = adjState.getValue(DoorBlock.FACING); if (adjState.getValue(DoorBlock.FACING) == facing
if (adjFacing == facing.getOpposite()) { && adjState.getValue(DoorBlock.HINGE) != hinge) {
return adjPos; return adjPos;
} }
} }
@@ -11,7 +11,7 @@ public class ShelterGoal extends Goal {
private final EllieEntity ellie; private final EllieEntity ellie;
private BlockPos shelterPos; private BlockPos shelterPos;
private static final int SEARCH_RADIUS = 12; private static final int SEARCH_RADIUS = 12;
private static final double REACH_DIST_SQR = 1.5 * 1.5; private static final double REACH_DIST_SQR = 0.75 * 0.75;
public ShelterGoal(EllieEntity ellie) { public ShelterGoal(EllieEntity ellie) {
this.ellie = ellie; this.ellie = ellie;
@@ -23,8 +23,7 @@ public class ShelterGoal extends Goal {
if (ellie.tickCount < 20) return false; if (ellie.tickCount < 20) return false;
if (ellie.isSleeping()) return false; if (ellie.isSleeping()) return false;
if (!ellie.level().isRaining() && !ellie.level().isThundering()) return false; if (!ellie.level().isRaining() && !ellie.level().isThundering()) return false;
if (!ellie.isInRain()) return false; return ellie.level().isRainingAt(ellie.blockPosition().above());
return true;
} }
@Override @Override
@@ -38,10 +37,23 @@ public class ShelterGoal extends Goal {
@Override @Override
public void tick() { public void tick() {
if (shelterPos == null) return; if (shelterPos == null) return;
Vec3 target = new Vec3(shelterPos.getX() + 0.5, shelterPos.getY(), shelterPos.getZ() + 0.5);
if (ellie.distanceToSqr(target) < REACH_DIST_SQR) { if (!ellie.level().isRainingAt(ellie.blockPosition().above())) {
ellie.getNavigation().stop(); ellie.getNavigation().stop();
} else if (ellie.getNavigation().isDone()) { return;
}
Vec3 target = new Vec3(shelterPos.getX() + 0.5, shelterPos.getY(), shelterPos.getZ() + 0.5);
if (ellie.distanceToSqr(target) < REACH_DIST_SQR && ellie.getNavigation().isDone()) {
BlockPos deeper = findDeeperShelter();
if (deeper != null) {
shelterPos = deeper;
target = new Vec3(shelterPos.getX() + 0.5, shelterPos.getY(), shelterPos.getZ() + 0.5);
}
}
if (ellie.getNavigation().isDone()) {
ellie.getNavigation().moveTo(target.x, target.y, target.z, 0.6); ellie.getNavigation().moveTo(target.x, target.y, target.z, 0.6);
} }
} }
@@ -64,15 +76,15 @@ public class ShelterGoal extends Goal {
BlockPos nearest = null; BlockPos nearest = null;
double nearestDist = Double.MAX_VALUE; double nearestDist = Double.MAX_VALUE;
for (int x = -SEARCH_RADIUS; x <= SEARCH_RADIUS; x++) { for (int dx = -SEARCH_RADIUS; dx <= SEARCH_RADIUS; dx++) {
for (int z = -SEARCH_RADIUS; z <= SEARCH_RADIUS; z++) { for (int dz = -SEARCH_RADIUS; dz <= SEARCH_RADIUS; dz++) {
for (int dy = 2; dy >= -4; dy--) { for (int dy = 1; dy >= -4; dy--) {
mutable.set(entityPos.getX() + x, entityPos.getY() + dy, entityPos.getZ() + z); mutable.set(entityPos.getX() + dx, entityPos.getY() + dy, entityPos.getZ() + dz);
BlockPos standPos = mutable.above(); BlockPos standPos = mutable.above();
if (!ellie.level().isRainingAt(standPos) if (ellie.level().isEmptyBlock(standPos)
&& ellie.level().getBlockState(standPos).isAir() && !ellie.level().getBlockState(mutable).isAir()
&& !ellie.level().getBlockState(mutable).isAir()) { && !ellie.level().isRainingAt(standPos)) {
double dist = entityPos.distSqr(mutable); double dist = entityPos.distSqr(standPos);
if (dist < nearestDist) { if (dist < nearestDist) {
nearestDist = dist; nearestDist = dist;
nearest = standPos; nearest = standPos;
@@ -84,4 +96,28 @@ public class ShelterGoal extends Goal {
} }
return nearest; return nearest;
} }
private BlockPos findDeeperShelter() {
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
BlockPos best = null;
double bestScore = 0;
for (int dx = -2; dx <= 2; dx++) {
for (int dz = -2; dz <= 2; dz++) {
if (dx == 0 && dz == 0) continue;
mutable.set(shelterPos.getX() + dx, shelterPos.getY(), shelterPos.getZ() + dz);
BlockPos standPos = mutable.below();
if (ellie.level().isEmptyBlock(mutable)
&& !ellie.level().getBlockState(standPos).isAir()
&& !ellie.level().isRainingAt(mutable)) {
double score = Math.abs(dx) + Math.abs(dz);
if (score > bestScore) {
bestScore = score;
best = mutable.immutable();
}
}
}
}
return best;
}
} }