Initial commit: Forge 1.20.1 Ellie companion mod
- EllieEntity with GeckoLib animations, sleep AI, pathfinding with crouching - Dialog system with conditions and effects - Relationship system with milestones - OpenDoor and bed occupation pathfinding - 15 animations: idle1/2/3, sleep, walkingsimple, shiftwalking/shiftidle, etc.
This commit is contained in:
@@ -0,0 +1,143 @@
|
||||
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.DoorBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
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.level().isNight() && !ellie.isTired()) return false;
|
||||
if (ellie.isSleeping()) return false;
|
||||
if (ellie.hurtTime > 0) return false;
|
||||
bedPos = findNearestBed();
|
||||
return bedPos != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
claimed = false;
|
||||
if (bedPos != null) {
|
||||
if (ellie.isSpaceLow(bedPos)) {
|
||||
ellie.setPose(net.minecraft.world.entity.Pose.CROUCHING);
|
||||
}
|
||||
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (bedPos == null) return;
|
||||
|
||||
// Open doors in path
|
||||
tryOpenDoor();
|
||||
|
||||
double distSqr = ellie.distanceToSqr(Vec3.atCenterOf(bedPos));
|
||||
|
||||
if (distSqr < REACH_DIST_SQR) {
|
||||
ellie.getNavigation().stop();
|
||||
|
||||
if (!claimed) {
|
||||
claimed = ellie.occupyBed(bedPos);
|
||||
}
|
||||
|
||||
if (claimed) {
|
||||
if (!ellie.isSleeping()) {
|
||||
ellie.setBedSleepPos(bedPos);
|
||||
}
|
||||
sleepTimer++;
|
||||
}
|
||||
} else {
|
||||
if (ellie.getNavigation().isDone()) {
|
||||
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryOpenDoor() {
|
||||
BlockPos inFront = ellie.blockPosition().relative(ellie.getDirection());
|
||||
for (int i = 0; i < 3; i++) {
|
||||
BlockPos checkPos = ellie.blockPosition().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 (ellie.hurtTime > 0) return false;
|
||||
if (ellie.isSleeping()) {
|
||||
if (sleepTimer >= MAX_SLEEP_TICKS) return false;
|
||||
if (bedPos != null && !ellie.level().getBlockState(bedPos).isBed(ellie.level(), bedPos, null)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
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.isBed(ellie.level(), pos, null)) return false;
|
||||
if (state.hasProperty(DoorBlock.OPEN) && state.getValue(DoorBlock.OPEN)) return false;
|
||||
return !state.getValue(net.minecraft.world.level.block.BedBlock.OCCUPIED);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user