Fix door stuck nudge, double door priority, shelter detection, and debug stick
This commit is contained in:
@@ -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,22 +140,43 @@ 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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
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 = mutable.immutable();
|
||||
nearest = standPos;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,11 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.INT);
|
||||
|
||||
private static final List<String> 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) {
|
||||
|
||||
@@ -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]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user