diff --git a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieDoorInteractGoal.java b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieDoorInteractGoal.java index f0b7edd..54e7dd9 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieDoorInteractGoal.java +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieDoorInteractGoal.java @@ -6,6 +6,7 @@ import net.minecraft.core.Direction; import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.level.block.DoorBlock; 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.Path; import net.minecraft.world.phys.Vec3; @@ -15,7 +16,7 @@ import java.util.EnumSet; public class EllieDoorInteractGoal extends Goal { private static final int CLOSE_DELAY = 25; 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 BlockPos doorPos; @@ -24,7 +25,7 @@ public class EllieDoorInteractGoal extends Goal { private int closeTimer; private Vec3 doorStuckPos; private int doorStuckTicks; - private boolean stuckNudgeCw; + private boolean lastNudgeWasCw; public EllieDoorInteractGoal(EllieEntity ellie) { this.ellie = ellie; @@ -57,7 +58,7 @@ public class EllieDoorInteractGoal extends Goal { hasPassed = true; closeTimer = CLOSE_DELAY; } else { - checkStuck(); + checkDoorStuck(); } } else if (--closeTimer <= 0) { closeDoor(doorPos); @@ -87,40 +88,39 @@ public class EllieDoorInteractGoal extends Goal { hasPassed = false; closeTimer = 0; + doorStuckPos = null; + doorStuckTicks = 0; } } - private void checkStuck() { + private void checkDoorStuck() { Vec3 current = ellie.position(); - if (doorStuckPos != null) { - if (current.distanceToSqr(doorStuckPos) < STUCK_DELTA) { - doorStuckTicks++; - if (doorStuckTicks > 40) { - nudgeDoor(); - doorStuckTicks = 0; - } - } else { + if (doorStuckPos != null && current.distanceToSqr(doorStuckPos) < 0.005) { + doorStuckTicks++; + if (doorStuckTicks > STUCK_TICK_LIMIT) { + nudgeFromDoor(); doorStuckTicks = 0; } + } else { + doorStuckTicks = 0; } doorStuckPos = current; } - private void nudgeDoor() { + private void nudgeFromDoor() { BlockState state = ellie.level().getBlockState(doorPos); if (!(state.getBlock() instanceof DoorBlock)) return; Direction facing = state.getValue(DoorBlock.FACING); - Direction nudgeDir = stuckNudgeCw - ? facing.getClockWise() - : facing.getCounterClockWise(); - stuckNudgeCw = !stuckNudgeCw; + Direction nudgeDir = lastNudgeWasCw ? facing.getCounterClockWise() : facing.getClockWise(); + lastNudgeWasCw = !lastNudgeWasCw; - ellie.setPos( - ellie.getX() + nudgeDir.getStepX() * 0.2, - ellie.getY(), - ellie.getZ() + nudgeDir.getStepZ() * 0.2 - ); + double nx = nudgeDir.getStepX() * 0.125; + double nz = nudgeDir.getStepZ() * 0.125; + ellie.setPos(ellie.getX() + nx, ellie.getY(), ellie.getZ() + nz); + + 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) { @@ -149,19 +149,20 @@ public class EllieDoorInteractGoal extends Goal { if (door != null) return door; } } + BlockPos center = ellie.blockPosition(); BlockPos singleDoor = null; for (int dx = -2; dx <= 2; dx++) { for (int dz = -2; dz <= 2; dz++) { for (int dy = -1; dy <= 1; dy++) { BlockPos checkPos = center.offset(dx, dy, dz); - if (!checkForDoor(checkPos)) continue; - BlockState state = ellie.level().getBlockState(checkPos); - if (findPairedDoor(checkPos, state) != null) { - return checkPos; - } - if (singleDoor == null) { - singleDoor = checkPos; + if (checkForDoor(checkPos)) { + if (isDoubleDoor(checkPos)) { + return checkPos; + } + if (singleDoor == null) { + singleDoor = checkPos; + } } } } @@ -169,6 +170,11 @@ public class EllieDoorInteractGoal extends Goal { return singleDoor; } + private boolean isDoubleDoor(BlockPos pos) { + BlockState state = ellie.level().getBlockState(pos); + return findPairedDoor(pos, state) != null; + } + private BlockPos resolveDoor(BlockPos pos) { if (checkForDoor(pos)) return pos; if (checkForDoor(pos.above())) return pos.above(); @@ -188,12 +194,13 @@ public class EllieDoorInteractGoal extends Goal { private BlockPos findPairedDoor(BlockPos pos, BlockState state) { Direction facing = state.getValue(DoorBlock.FACING); + DoorHingeSide hinge = state.getValue(DoorBlock.HINGE); for (Direction adj : Direction.Plane.HORIZONTAL) { BlockPos adjPos = pos.relative(adj); BlockState adjState = ellie.level().getBlockState(adjPos); if (adjState.getBlock() instanceof DoorBlock) { - Direction adjFacing = adjState.getValue(DoorBlock.FACING); - if (adjFacing == facing.getOpposite()) { + if (adjState.getValue(DoorBlock.FACING) == facing + && adjState.getValue(DoorBlock.HINGE) != hinge) { return adjPos; } } diff --git a/src/main/java/me/sashegdev/fabled_hearts/ai/ShelterGoal.java b/src/main/java/me/sashegdev/fabled_hearts/ai/ShelterGoal.java index 6ea360b..8740902 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/ai/ShelterGoal.java +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/ShelterGoal.java @@ -11,7 +11,7 @@ public class ShelterGoal extends Goal { private final EllieEntity ellie; private BlockPos shelterPos; 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) { this.ellie = ellie; @@ -23,8 +23,7 @@ public class ShelterGoal extends Goal { if (ellie.tickCount < 20) return false; if (ellie.isSleeping()) return false; if (!ellie.level().isRaining() && !ellie.level().isThundering()) return false; - if (!ellie.isInRain()) return false; - return true; + return ellie.level().isRainingAt(ellie.blockPosition().above()); } @Override @@ -38,10 +37,23 @@ public class ShelterGoal extends Goal { @Override public void tick() { 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(); - } 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); } } @@ -64,15 +76,15 @@ public class ShelterGoal extends Goal { BlockPos nearest = null; double nearestDist = Double.MAX_VALUE; - for (int x = -SEARCH_RADIUS; x <= SEARCH_RADIUS; x++) { - for (int z = -SEARCH_RADIUS; z <= SEARCH_RADIUS; z++) { - for (int dy = 2; dy >= -4; dy--) { - mutable.set(entityPos.getX() + x, entityPos.getY() + dy, entityPos.getZ() + z); + for (int dx = -SEARCH_RADIUS; dx <= SEARCH_RADIUS; dx++) { + for (int dz = -SEARCH_RADIUS; dz <= SEARCH_RADIUS; dz++) { + for (int dy = 1; dy >= -4; dy--) { + mutable.set(entityPos.getX() + dx, entityPos.getY() + dy, entityPos.getZ() + dz); BlockPos standPos = mutable.above(); - if (!ellie.level().isRainingAt(standPos) - && ellie.level().getBlockState(standPos).isAir() - && !ellie.level().getBlockState(mutable).isAir()) { - double dist = entityPos.distSqr(mutable); + if (ellie.level().isEmptyBlock(standPos) + && !ellie.level().getBlockState(mutable).isAir() + && !ellie.level().isRainingAt(standPos)) { + double dist = entityPos.distSqr(standPos); if (dist < nearestDist) { nearestDist = dist; nearest = standPos; @@ -84,4 +96,28 @@ public class ShelterGoal extends Goal { } 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; + } }