180 lines
6.0 KiB
Java
180 lines
6.0 KiB
Java
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.block.BedBlock;
|
|
import net.minecraft.world.level.block.DoorBlock;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.pathfinder.Node;
|
|
import net.minecraft.world.level.pathfinder.Path;
|
|
import net.minecraft.world.phys.Vec3;
|
|
|
|
import java.util.EnumSet;
|
|
|
|
public class EllieSleepGoal extends Goal {
|
|
private final EllieEntity ellie;
|
|
private final int searchRadius;
|
|
private BlockPos bedPos;
|
|
private int sleepTimer;
|
|
private boolean claimed;
|
|
private static final int MAX_SLEEP_TICKS = 6000;
|
|
private static final double REACH_DIST_SQR = 1.8 * 1.8;
|
|
|
|
public EllieSleepGoal(EllieEntity ellie, int searchRadius) {
|
|
this.ellie = ellie;
|
|
this.searchRadius = searchRadius;
|
|
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
|
}
|
|
|
|
@Override
|
|
public boolean canUse() {
|
|
if (ellie.isSleeping()) return false;
|
|
if (!ellie.level().isNight() && !ellie.isTired()) return false;
|
|
|
|
bedPos = findBed();
|
|
return bedPos != null;
|
|
}
|
|
|
|
private BlockPos findBed() {
|
|
BlockPos stored = ellie.getBedPos();
|
|
if (stored != null && isFreeBed(stored) && ellie.distanceToSqr(Vec3.atCenterOf(stored)) < 50 * 50) {
|
|
return stored;
|
|
}
|
|
return findNearestBed();
|
|
}
|
|
|
|
@Override
|
|
public void start() {
|
|
claimed = false;
|
|
if (bedPos != null) {
|
|
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (bedPos == null) return;
|
|
|
|
BlockState currentState = ellie.level().getBlockState(bedPos);
|
|
if (!(currentState.getBlock() instanceof BedBlock)) {
|
|
bedPos = null;
|
|
return;
|
|
}
|
|
|
|
tryOpenDoor();
|
|
|
|
double distSqr = ellie.distanceToSqr(Vec3.atCenterOf(bedPos));
|
|
|
|
if (distSqr < REACH_DIST_SQR) {
|
|
ellie.getNavigation().stop();
|
|
|
|
if (!claimed) {
|
|
claimed = ellie.occupyBed(bedPos);
|
|
}
|
|
|
|
if (claimed && !ellie.isSleeping()) {
|
|
ellie.setBedSleepPos(bedPos);
|
|
}
|
|
|
|
if (ellie.isSleeping()) {
|
|
sleepTimer++;
|
|
}
|
|
} else {
|
|
if (ellie.getNavigation().isDone()) {
|
|
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void tryOpenDoor() {
|
|
Path path = ellie.getNavigation().getPath();
|
|
BlockPos entityPos = ellie.blockPosition();
|
|
if (path != null) {
|
|
for (int i = 0; i < Math.min(4, path.getNodeCount()); i++) {
|
|
Node node = path.getNode(i);
|
|
BlockPos checkPos = new BlockPos(node.x, node.y, node.z);
|
|
if (checkPos.distSqr(entityPos) > 9) break;
|
|
BlockState state = ellie.level().getBlockState(checkPos);
|
|
if (state.getBlock() instanceof DoorBlock) {
|
|
if (!state.getValue(DoorBlock.OPEN)) {
|
|
ellie.level().setBlock(checkPos, state.setValue(DoorBlock.OPEN, true), 3);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
for (int i = 0; i < 3; i++) {
|
|
BlockPos checkPos = entityPos.relative(ellie.getDirection(), i);
|
|
BlockState state = ellie.level().getBlockState(checkPos);
|
|
if (state.getBlock() instanceof DoorBlock) {
|
|
if (!state.getValue(DoorBlock.OPEN)) {
|
|
ellie.level().setBlock(checkPos, state.setValue(DoorBlock.OPEN, true), 3);
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void stop() {
|
|
if (ellie.isSleeping()) {
|
|
ellie.wakeUp();
|
|
}
|
|
sleepTimer = 0;
|
|
bedPos = null;
|
|
claimed = false;
|
|
}
|
|
|
|
@Override
|
|
public boolean canContinueToUse() {
|
|
if (bedPos != null) {
|
|
BlockState state = ellie.level().getBlockState(bedPos);
|
|
if (!state.isBed(ellie.level(), bedPos, null)) return false;
|
|
}
|
|
|
|
if (ellie.isSleeping()) {
|
|
if (sleepTimer >= MAX_SLEEP_TICKS) return false;
|
|
if (ellie.level().isDay()) return false;
|
|
if (ellie.hurtTime > 10) return false;
|
|
if (ellie.distanceToSqr(Vec3.atCenterOf(bedPos)) > 16.0) return false;
|
|
return true;
|
|
}
|
|
if (bedPos == null) return false;
|
|
if (ellie.hurtTime > 10) return false;
|
|
if (ellie.level().isDay() && !ellie.isTired()) return false;
|
|
if (!ellie.level().isNight() && !ellie.isTired()) return false;
|
|
return true;
|
|
}
|
|
|
|
private BlockPos findNearestBed() {
|
|
BlockPos entityPos = ellie.blockPosition();
|
|
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
|
BlockPos nearest = null;
|
|
double nearestDist = Double.MAX_VALUE;
|
|
|
|
for (int x = -searchRadius; x <= searchRadius; x++) {
|
|
for (int z = -searchRadius; z <= searchRadius; z++) {
|
|
for (int y = -2; y <= 2; y++) {
|
|
mutable.set(entityPos.getX() + x, entityPos.getY() + y, entityPos.getZ() + z);
|
|
if (isFreeBed(mutable)) {
|
|
double dist = entityPos.distSqr(mutable);
|
|
if (dist < nearestDist) {
|
|
nearestDist = dist;
|
|
nearest = mutable.immutable();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return nearest;
|
|
}
|
|
|
|
private boolean isFreeBed(BlockPos pos) {
|
|
BlockState state = ellie.level().getBlockState(pos);
|
|
if (!(state.getBlock() instanceof BedBlock)) return false;
|
|
if (state.getValue(BedBlock.OCCUPIED)) return false;
|
|
return true;
|
|
}
|
|
}
|