diff --git a/src/main/java/me/sashegdev/fabled_hearts/Main.java b/src/main/java/me/sashegdev/fabled_hearts/Main.java index 7133ecd..86960d2 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/Main.java +++ b/src/main/java/me/sashegdev/fabled_hearts/Main.java @@ -78,7 +78,10 @@ public class Main { EllieEntity.class, new net.minecraft.world.phys.AABB(pos).inflate(2), e -> e.isSleeping() && pos.equals(e.getBedPos()) - ).forEach(EllieEntity::wakeUp); + ).forEach(e -> { + e.wakeUp(); + e.forgetBed(); + }); } } } diff --git a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieSleepGoal.java b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieSleepGoal.java index 5b9eb30..6a6b4a5 100644 --- a/src/main/java/me/sashegdev/fabled_hearts/ai/EllieSleepGoal.java +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/EllieSleepGoal.java @@ -29,10 +29,19 @@ public class EllieSleepGoal extends Goal { if (!ellie.level().isNight() && !ellie.isTired()) return false; if (ellie.isSleeping()) return false; if (ellie.hurtTime > 0) return false; - bedPos = findNearestBed(); + + 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; diff --git a/src/main/java/me/sashegdev/fabled_hearts/ai/FollowPlayerGoal.java b/src/main/java/me/sashegdev/fabled_hearts/ai/FollowPlayerGoal.java new file mode 100644 index 0000000..ae97e28 --- /dev/null +++ b/src/main/java/me/sashegdev/fabled_hearts/ai/FollowPlayerGoal.java @@ -0,0 +1,98 @@ +package me.sashegdev.fabled_hearts.ai; + +import me.sashegdev.fabled_hearts.entity.ellie.EllieEntity; +import net.minecraft.world.entity.ai.goal.Goal; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.phys.Vec3; + +import java.util.EnumSet; + +public class FollowPlayerGoal extends Goal { + private final EllieEntity ellie; + private Player target; + private int cooldown; + private static final double FOLLOW_RANGE = 16.0; + private static final double FOLLOW_RANGE_SQR = FOLLOW_RANGE * FOLLOW_RANGE; + + public FollowPlayerGoal(EllieEntity ellie) { + this.ellie = ellie; + this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK)); + } + + @Override + public boolean canUse() { + if (ellie.isSleeping()) return false; + if (ellie.hurtTime > 0) return false; + float rel = ellie.getRelationshipPoints(); + if (rel < 50) return false; + + Player nearest = ellie.level().getNearestPlayer(ellie, FOLLOW_RANGE); + if (nearest == null) return false; + target = nearest; + return true; + } + + @Override + public void start() { + cooldown = 0; + } + + @Override + public void tick() { + if (target == null) return; + + double distSqr = ellie.distanceToSqr(target); + float rel = ellie.getRelationshipPoints(); + + double minDist, maxDist; + if (rel >= 100) { + minDist = 1.5; maxDist = 3.0; + } else if (rel >= 75) { + minDist = 3.0; maxDist = 6.0; + } else { + minDist = 6.0; maxDist = 10.0; + } + + if (distSqr > maxDist * maxDist) { + if (--cooldown <= 0) { + cooldown = 10; + ellie.getNavigation().moveTo(target, 0.6); + } + } else if (distSqr < minDist * minDist) { + ellie.getNavigation().stop(); + if (--cooldown <= 0) { + cooldown = 10; + Vec3 away = new Vec3( + ellie.getX() - target.getX(), + 0, + ellie.getZ() - target.getZ() + ).normalize(); + ellie.getNavigation().moveTo( + target.getX() + away.x * maxDist, + target.getY(), + target.getZ() + away.z * maxDist, + 0.4 + ); + } + } else { + ellie.getNavigation().stop(); + cooldown = 0; + ellie.getLookControl().setLookAt(target, 30, 30); + } + } + + @Override + public boolean canContinueToUse() { + if (target == null || !target.isAlive()) return false; + if (ellie.isSleeping()) return false; + if (ellie.hurtTime > 0) return false; + double distSqr = ellie.distanceToSqr(target); + return distSqr <= FOLLOW_RANGE_SQR; + } + + @Override + public void stop() { + target = null; + ellie.getNavigation().stop(); + } +} 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 7076ab5..e179ef0 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 @@ -92,8 +92,9 @@ public class EllieEntity extends Animal implements GeoEntity { @Override protected void registerGoals() { BasicAI.addBasicGoals(this, this.goalSelector, 2); - this.goalSelector.addGoal(1, new EllieSleepGoal(this, 20)); + this.goalSelector.addGoal(1, new EllieSleepGoal(this, 30)); this.goalSelector.addGoal(1, new IdleAnimationGoal(this)); + this.goalSelector.addGoal(3, new me.sashegdev.fabled_hearts.ai.FollowPlayerGoal(this)); } @Override @@ -265,10 +266,13 @@ public class EllieEntity extends Animal implements GeoEntity { } } } - bedPos = null; } } + public void forgetBed() { + bedPos = null; + } + public boolean isBedAt(BlockPos pos) { return level().getBlockState(pos).isBed(level(), pos, null); } @@ -421,6 +425,7 @@ public class EllieEntity extends Animal implements GeoEntity { public boolean isTired() { return entityData.get(DATA_TIRED); } public boolean isInRain() { return entityData.get(DATA_IN_RAIN); } public boolean isUnderLowCeiling() { return entityData.get(DATA_UNDER_LOW_CEILING); } + public void setBedPos(BlockPos pos) { this.bedPos = pos; } public BlockPos getBedPos() { return bedPos; } public float getRelationshipPoints() { return relationshipPoints; } public void addRelationshipPoints(float amount) {