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 df87281..f0b7edd 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieDoorInteractGoal.java +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieDoorInteractGoal.java @@ -15,14 +15,16 @@ 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 WAYPOINT_REACH_DIST = 0.4; + private static final double STUCK_DELTA = 0.005; private final EllieEntity ellie; private BlockPos doorPos; private BlockPos pairedDoorPos; - private Vec3 passageCenter; private boolean hasPassed; private int closeTimer; + private Vec3 doorStuckPos; + private int doorStuckTicks; + private boolean stuckNudgeCw; public EllieDoorInteractGoal(EllieEntity ellie) { this.ellie = ellie; @@ -49,21 +51,13 @@ public class EllieDoorInteractGoal extends Goal { return; } - if (passageCenter != null) { - boolean reached = ellie.distanceToSqr(passageCenter) < WAYPOINT_REACH_DIST * WAYPOINT_REACH_DIST - || ellie.getNavigation().isDone(); - if (reached) { - passageCenter = null; - } else { - ellie.getNavigation().moveTo(passageCenter.x, passageCenter.y, passageCenter.z, 0.6); - } - } - if (!hasPassed) { double distSq = ellie.distanceToSqr(Vec3.atCenterOf(doorPos)); if (distSq > CLOSE_DIST_SQ) { hasPassed = true; closeTimer = CLOSE_DELAY; + } else { + checkStuck(); } } else if (--closeTimer <= 0) { closeDoor(doorPos); @@ -93,11 +87,42 @@ public class EllieDoorInteractGoal extends Goal { hasPassed = false; closeTimer = 0; - passageCenter = calcPassageCenter(); - ellie.getNavigation().moveTo(passageCenter.x, passageCenter.y, passageCenter.z, 0.6); } } + private void checkStuck() { + Vec3 current = ellie.position(); + if (doorStuckPos != null) { + if (current.distanceToSqr(doorStuckPos) < STUCK_DELTA) { + doorStuckTicks++; + if (doorStuckTicks > 40) { + nudgeDoor(); + doorStuckTicks = 0; + } + } else { + doorStuckTicks = 0; + } + } + doorStuckPos = current; + } + + private void nudgeDoor() { + 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; + + ellie.setPos( + ellie.getX() + nudgeDir.getStepX() * 0.2, + ellie.getY(), + ellie.getZ() + nudgeDir.getStepZ() * 0.2 + ); + } + private void openDoor(BlockPos pos, BlockState state) { if (state.getBlock() instanceof DoorBlock door && !state.getValue(DoorBlock.OPEN)) { door.setOpen(ellie, ellie.level(), state, pos, true); @@ -115,23 +140,44 @@ public class EllieDoorInteractGoal extends Goal { Path path = ellie.getNavigation().getPath(); if (path != null && !path.isDone()) { BlockPos entityPos = ellie.blockPosition(); - for (int i = 0; i < Math.min(8, path.getNodeCount()); i++) { + for (int i = 0; i < Math.min(16, path.getNodeCount()); i++) { Node node = path.getNode(i); if (node == null) continue; BlockPos checkPos = new BlockPos(node.x, node.y, node.z); if (checkPos.distSqr(entityPos) > 64) continue; - if (checkForDoor(checkPos)) return checkPos; - if (checkForDoor(checkPos.above())) return checkPos.above(); + BlockPos door = resolveDoor(checkPos); + if (door != null) return door; } } BlockPos center = ellie.blockPosition(); - for (int dx = -1; dx <= 1; dx++) { - for (int dz = -1; dz <= 1; dz++) { - BlockPos checkPos = center.offset(dx, 0, dz); - if (checkForDoor(checkPos)) return checkPos; - if (checkForDoor(checkPos.above())) return checkPos.above(); + 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; + } + } } } + return singleDoor; + } + + private BlockPos resolveDoor(BlockPos pos) { + if (checkForDoor(pos)) return pos; + if (checkForDoor(pos.above())) return pos.above(); + if (checkForDoor(pos.below())) return pos.below(); + for (Direction dir : Direction.Plane.HORIZONTAL) { + BlockPos adj = pos.relative(dir); + if (checkForDoor(adj)) return adj; + if (checkForDoor(adj.above())) return adj.above(); + } return null; } @@ -155,19 +201,13 @@ public class EllieDoorInteractGoal extends Goal { return null; } - private Vec3 calcPassageCenter() { - if (pairedDoorPos != null) { - return Vec3.atCenterOf(doorPos).add(Vec3.atCenterOf(pairedDoorPos)).scale(0.5); - } - return Vec3.atCenterOf(doorPos); - } - private void clear() { doorPos = null; pairedDoorPos = null; - passageCenter = null; hasPassed = false; closeTimer = 0; + doorStuckPos = null; + doorStuckTicks = 0; } @Override diff --git a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieNodeEvaluator.java b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieNodeEvaluator.java index 34fb86b..4dae9b3 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieNodeEvaluator.java +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieNodeEvaluator.java @@ -14,9 +14,17 @@ public class EllieNodeEvaluator extends WalkNodeEvaluator { if (node != null) return node; BlockPathTypes head1 = this.getCachedBlockType(this.mob, x, y + 1, z); - if (head1 == BlockPathTypes.BLOCKED) return null; + float head1Malus = this.mob.getPathfindingMalus(head1); + if (head1Malus < 0.0f) { + BlockPathTypes body = this.getCachedBlockType(this.mob, x, y, z); + BlockPathTypes below = this.getCachedBlockType(this.mob, x, y - 1, z); + if (this.mob.getPathfindingMalus(body) < 0.0f || this.mob.getPathfindingMalus(below) < 0.0f) { + return null; + } + } + BlockPathTypes head2 = this.getCachedBlockType(this.mob, x, y + 2, z); - if (head2 != BlockPathTypes.BLOCKED) return null; + if (this.mob.getPathfindingMalus(head2) >= 0.0f) return null; BlockPathTypes footType = this.getCachedBlockType(this.mob, x, y, z); float costMalus = this.mob.getPathfindingMalus(footType); 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 4b5f36b..6ea360b 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/ai/ShelterGoal.java +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/ShelterGoal.java @@ -3,8 +3,6 @@ package me.sashegdev.fabled_hearts.ai; import me.sashegdev.fabled_hearts.entity.ellie.EllieEntity; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.ai.goal.Goal; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.phys.Vec3; import java.util.EnumSet; @@ -40,10 +38,11 @@ public class ShelterGoal extends Goal { @Override public void tick() { if (shelterPos == null) return; - if (ellie.distanceToSqr(Vec3.atCenterOf(shelterPos)) < REACH_DIST_SQR) { + Vec3 target = new Vec3(shelterPos.getX() + 0.5, shelterPos.getY(), shelterPos.getZ() + 0.5); + if (ellie.distanceToSqr(target) < REACH_DIST_SQR) { ellie.getNavigation().stop(); } else if (ellie.getNavigation().isDone()) { - ellie.getNavigation().moveTo(shelterPos.getX() + 0.5, shelterPos.getY(), shelterPos.getZ() + 0.5, 0.6); + ellie.getNavigation().moveTo(target.x, target.y, target.z, 0.6); } } @@ -59,16 +58,6 @@ public class ShelterGoal extends Goal { shelterPos = null; } - private boolean hasShelter(BlockPos pos) { - Level level = ellie.level(); - for (int y = 1; y <= 3; y++) { - if (level.getBlockState(pos.above(y)).blocksMotion()) { - return true; - } - } - return false; - } - private BlockPos findShelter() { BlockPos entityPos = ellie.blockPosition(); BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); @@ -77,14 +66,18 @@ public class ShelterGoal extends Goal { for (int x = -SEARCH_RADIUS; x <= SEARCH_RADIUS; x++) { for (int z = -SEARCH_RADIUS; z <= SEARCH_RADIUS; z++) { - mutable.set(entityPos.getX() + x, entityPos.getY(), entityPos.getZ() + z); - int topY = ellie.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, mutable).getY(); - mutable.setY(topY); - if (hasShelter(mutable)) { - double dist = entityPos.distSqr(mutable); - if (dist < nearestDist) { - nearestDist = dist; - nearest = mutable.immutable(); + for (int dy = 2; dy >= -4; dy--) { + mutable.set(entityPos.getX() + x, entityPos.getY() + dy, entityPos.getZ() + z); + 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 (dist < nearestDist) { + nearestDist = dist; + nearest = standPos; + } + break; } } } diff --git a/src/main/java/me/sashegdev/fabled_hearts/entity/ellie/EllieEntity.java b/src/main/java/me/sashegdev/fabled_hearts/entity/ellie/EllieEntity.java index 478d850..92709a1 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/entity/ellie/EllieEntity.java +++ b/src/main/java/me/sashegdev/fabled_hearts/entity/ellie/EllieEntity.java @@ -77,7 +77,11 @@ public class EllieEntity extends Animal implements GeoEntity { SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.INT); private static final List DEBUG_ANIMS = List.of( - "idle1", "idle2", "idle3", "turn", "eat1", "fall" + "idle1", "idle2", "idle3", "turn", "eat1", "fall", + "sleep", "walkingsleepy", "idlesleepy", + "walkingrain", "idleconrain", + "shiftwalking", "shiftidle", + "walkingsimple", "idleins" ); private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this); @@ -423,7 +427,7 @@ public class EllieEntity extends Animal implements GeoEntity { BlockPos footPos = headPos.relative(facing.getOpposite()); double footX = footPos.getX() + 0.5 - facing.getStepX() * 0.5; double footZ = footPos.getZ() + 0.5 - facing.getStepZ() * 0.5; - setPos(footX, footPos.getY() + 0.6875, footZ); + setPos(footX, footPos.getY() + 1.0625, footZ); setSleepingPos(headPos); float yRot = facing.getOpposite().toYRot(); setYRot(yRot); @@ -530,7 +534,7 @@ public class EllieEntity extends Animal implements GeoEntity { @Override public InteractionResult mobInteract(Player player, InteractionHand hand) { ItemStack stack = player.getItemInHand(hand); - boolean holdingStick = stack.is(Items.STICK); + boolean holdingStick = stack.is(Items.DEBUG_STICK); if (holdingStick && player.isShiftKeyDown()) { if (!level().isClientSide) { @@ -547,7 +551,7 @@ public class EllieEntity extends Animal implements GeoEntity { if (holdingStick) { if (!level().isClientSide) { int current = entityData.get(DATA_DEBUG_ANIM); - int next = (current % DEBUG_ANIMS.size()) + 1; + int next = (current + 1) % (DEBUG_ANIMS.size() + 1); entityData.set(DATA_DEBUG_ANIM, next); debugAnimTimer = 200; } @@ -575,7 +579,7 @@ public class EllieEntity extends Animal implements GeoEntity { public boolean hurt(DamageSource source, float amount) { if (source.getEntity() instanceof Player player) { ItemStack weapon = player.getMainHandItem(); - if (weapon.is(Items.STICK)) { + if (weapon.is(Items.DEBUG_STICK)) { if (!level().isClientSide) { int current = entityData.get(DATA_DEBUG_ANIM); if (current > 0) { diff --git a/src/main/resources/assets/fabled_hearts/animations/ellie.animation.json b/src/main/resources/assets/fabled_hearts/animations/ellie.animation.json index e039913..753ea21 100644 --- a/src/main/resources/assets/fabled_hearts/animations/ellie.animation.json +++ b/src/main/resources/assets/fabled_hearts/animations/ellie.animation.json @@ -13899,53 +13899,97 @@ "bones": { "root": { "rotation": { - "vector": [0, 0, 0] + "vector": [90, 0, -180] }, "position": { - "vector": [0, 0, 0] + "vector": [0, 9.1, 9] } }, "torso": { "rotation": { - "0.0833": { "vector": [0, 0, -5] }, - "1.0833": { "vector": [2, 0, -5] }, - "1.3333": { "vector": [2.25, 0, -5] }, - "2.0833": { "vector": [0, 0, -5] } + "0.0833": { + "vector": [0, 0, -5] + }, + "1.0833": { + "vector": [2, 0, -5] + }, + "1.3333": { + "vector": [2.25, 0, -5] + }, + "2.0833": { + "vector": [0, 0, -5] + } }, "position": { - "0.0": { "vector": [0, -0.4, 0] }, - "1.0": { "vector": [0, -0.3, 0] }, - "1.25": { "vector": [0, -0.3, 0] }, - "2.0": { "vector": [0, -0.4, 0] } + "0.0": { + "vector": [0, -0.4, 0] + }, + "1.0": { + "vector": [0, -0.3, 0] + }, + "1.25": { + "vector": [0, -0.3, 0] + }, + "2.0": { + "vector": [0, -0.4, 0] + } } }, "head": { "rotation": { - "0.0": { "vector": [0, 0, -5] }, - "0.1667": { "vector": [0, 0, -5] }, - "1.1667": { "vector": [-3, 0, -5] }, - "1.4167": { "vector": [-3, 0, -5] }, - "2.1667": { "vector": [0, 0, -5] } + "0.0": { + "vector": [0, 0, -5] + }, + "0.1667": { + "vector": [0, 0, -5] + }, + "1.1667": { + "vector": [-3, 0, -5] + }, + "1.4167": { + "vector": [-3, 0, -5] + }, + "2.1667": { + "vector": [0, 0, -5] + } } }, "righteye": { - "position": { "vector": [0, 0, 1] } + "position": { + "vector": [0, 0, 1] + } }, "frown": { - "position": { "vector": [0, 0, 1] } + "position": { + "vector": [0, 0, 1] + } }, "rightarm": { "rotation": { - "0.0": { "vector": [0, 0, -10] }, - "0.2083": { "vector": [0, 0, -10] }, - "1.2083": { "vector": [2, 0, -10] }, - "1.4583": { "vector": [3, 0, -10] }, - "2.2083": { "vector": [0, 0, -10] } + "0.0": { + "vector": [0, 0, -10] + }, + "0.2083": { + "vector": [0, 0, -10] + }, + "1.2083": { + "vector": [2, 0, -10] + }, + "1.4583": { + "vector": [3, 0, -10] + }, + "2.2083": { + "vector": [0, 0, -10] + } } }, "rightleg": { - "rotation": { "vector": [0, 0, 25] }, - "position": { "vector": [0, 0.7, 0] } + "rotation": { + "vector": [0, 0, 25] + }, + "position": { + "vector": [0, 0.7, 0] + } }, "openmouth]2": { "position": { @@ -13985,19 +14029,19 @@ "leftarm": { "rotation": { "0.0": { - "vector": [0, -82.5, -80] + "vector": [-17.1045, 0.5398, 22.7077] }, "0.2083": { - "vector": [0, -82.5, -80] + "vector": [-17.1045, 0.5398, 22.7077] }, "1.2083": { - "vector": [18.3312, -82.0966, -98.4951] + "vector": [-14.6045, 0.5398, 22.7077] }, "1.4583": { - "vector": [24.8777, -81.7278, -105.1071] + "vector": [-14.6045, 0.5398, 22.7077] }, "2.2083": { - "vector": [0, -82.5, -80] + "vector": [-17.1045, 0.5398, 22.7077] } }, "position": { @@ -14007,6 +14051,12 @@ "0.2083": { "vector": [-1, 0, 0] }, + "1.2083": { + "vector": [-1, 0, 0] + }, + "1.4583": { + "vector": [-1, 0, 0] + }, "2.2083": { "vector": [-1, 0, 0] } @@ -14014,17 +14064,31 @@ }, "loverleftarm": { "rotation": { - "vector": [0, 0, 12.5] + "0.0": { + "vector": [0, 0, 85] + }, + "0.3333": { + "vector": [0, 0, 85] + }, + "1.3333": { + "vector": [0, 0, 87.5] + }, + "1.5833": { + "vector": [0, 0, 87.5] + }, + "2.25": { + "vector": [0, 0, 85] + } } }, "leftleg": { "rotation": { - "vector": [-30, 0, 0] + "vector": [-15, 0, 0] } }, "loverleg_L": { "rotation": { - "vector": [70, 0, 0] + "vector": [47.5, 0, 0] } }, "closed_L": { @@ -14036,6 +14100,14 @@ "position": { "vector": [0, 0, 1] } + }, + "clothleg_lower_L": { + "rotation": { + "vector": [42.5, 0, 0] + }, + "position": { + "vector": [0, -0.5, -0.3] + } } } },