Improve AI: bed memory (30-block search), relationship-based follow goal, crouch fixes
- EllieSleepGoal: check stored bedPos first, search 30 blocks on miss - releaseBed() no longer clears memory; forgetBed() for destruction - New FollowPlayerGoal: 50-75% distant follow, 75-99% closer, 100% approach - fix: crouch animation thenLoop, idle doesn't override crouching - fix: sleep Y rotation = facing.toYRot() (head toward headboard) - fix: checkLowCeiling scan range reduced (2-4 blocks, 3 for path)
This commit is contained in:
@@ -78,7 +78,10 @@ public class Main {
|
|||||||
EllieEntity.class,
|
EllieEntity.class,
|
||||||
new net.minecraft.world.phys.AABB(pos).inflate(2),
|
new net.minecraft.world.phys.AABB(pos).inflate(2),
|
||||||
e -> e.isSleeping() && pos.equals(e.getBedPos())
|
e -> e.isSleeping() && pos.equals(e.getBedPos())
|
||||||
).forEach(EllieEntity::wakeUp);
|
).forEach(e -> {
|
||||||
|
e.wakeUp();
|
||||||
|
e.forgetBed();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,10 +29,19 @@ public class EllieSleepGoal extends Goal {
|
|||||||
if (!ellie.level().isNight() && !ellie.isTired()) return false;
|
if (!ellie.level().isNight() && !ellie.isTired()) return false;
|
||||||
if (ellie.isSleeping()) return false;
|
if (ellie.isSleeping()) return false;
|
||||||
if (ellie.hurtTime > 0) return false;
|
if (ellie.hurtTime > 0) return false;
|
||||||
bedPos = findNearestBed();
|
|
||||||
|
bedPos = findBed();
|
||||||
return bedPos != null;
|
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
|
@Override
|
||||||
public void start() {
|
public void start() {
|
||||||
claimed = false;
|
claimed = false;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -92,8 +92,9 @@ public class EllieEntity extends Animal implements GeoEntity {
|
|||||||
@Override
|
@Override
|
||||||
protected void registerGoals() {
|
protected void registerGoals() {
|
||||||
BasicAI.addBasicGoals(this, this.goalSelector, 2);
|
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(1, new IdleAnimationGoal(this));
|
||||||
|
this.goalSelector.addGoal(3, new me.sashegdev.fabled_hearts.ai.FollowPlayerGoal(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -265,10 +266,13 @@ public class EllieEntity extends Animal implements GeoEntity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bedPos = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void forgetBed() {
|
||||||
|
bedPos = null;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isBedAt(BlockPos pos) {
|
public boolean isBedAt(BlockPos pos) {
|
||||||
return level().getBlockState(pos).isBed(level(), pos, null);
|
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 isTired() { return entityData.get(DATA_TIRED); }
|
||||||
public boolean isInRain() { return entityData.get(DATA_IN_RAIN); }
|
public boolean isInRain() { return entityData.get(DATA_IN_RAIN); }
|
||||||
public boolean isUnderLowCeiling() { return entityData.get(DATA_UNDER_LOW_CEILING); }
|
public boolean isUnderLowCeiling() { return entityData.get(DATA_UNDER_LOW_CEILING); }
|
||||||
|
public void setBedPos(BlockPos pos) { this.bedPos = pos; }
|
||||||
public BlockPos getBedPos() { return bedPos; }
|
public BlockPos getBedPos() { return bedPos; }
|
||||||
public float getRelationshipPoints() { return relationshipPoints; }
|
public float getRelationshipPoints() { return relationshipPoints; }
|
||||||
public void addRelationshipPoints(float amount) {
|
public void addRelationshipPoints(float amount) {
|
||||||
|
|||||||
Reference in New Issue
Block a user