Порт из Claude Code версии: укладывание на кровать, дом, ночной сон, sleepy после 2 ночей

This commit is contained in:
SashegDev
2026-08-16 07:15:45 +00:00
parent beaaef421c
commit 1019f04b09
9 changed files with 447 additions and 133 deletions
+3
View File
@@ -115,6 +115,9 @@ gradle-app.setting
run/ run/
runs/ runs/
# IDE build output
bin/
# Forge Gradle # Forge Gradle
crash-reports/ crash-reports/
logs/ logs/
Vendored Regular → Executable
View File
@@ -11,7 +11,6 @@ public class BasicAI {
selector.addGoal(p++, new FloatGoal(mob)); selector.addGoal(p++, new FloatGoal(mob));
selector.addGoal(p++, new LookAtPlayerGoal(mob, Player.class, 8.0f)); selector.addGoal(p++, new LookAtPlayerGoal(mob, Player.class, 8.0f));
selector.addGoal(p++, new RandomLookAroundGoal(mob)); selector.addGoal(p++, new RandomLookAroundGoal(mob));
selector.addGoal(p++, new WaterAvoidingRandomStrollGoal(mob, 0.8));
selector.addGoal(p++, new OpenDoorGoal(mob, true)); selector.addGoal(p++, new OpenDoorGoal(mob, true));
} }
} }
@@ -0,0 +1,84 @@
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.levelgen.Heightmap;
import net.minecraft.world.phys.Vec3;
import java.util.EnumSet;
/**
* Элли бродит свободно, но случайная цель всегда в пределах homeRadius от домашней точки
* (обычно — место спавна Элли / спавн игрока). Если её вынесло за зону — ведёт обратно домой.
*/
public class BoundedWanderGoal extends Goal {
private static final int WANDER_CHANCE = 60;
private final EllieEntity mob;
private final double speed;
private double targetX;
private double targetY;
private double targetZ;
public BoundedWanderGoal(EllieEntity mob, double speed) {
this.mob = mob;
this.speed = speed;
this.setFlags(EnumSet.of(Flag.MOVE));
}
@Override
public boolean canUse() {
if (this.mob.isSleeping()) {
return false;
}
if (!this.mob.getNavigation().isDone()) {
return false;
}
if (this.mob.getRandom().nextInt(WANDER_CHANCE) != 0) {
return false;
}
Vec3 target = pickTarget();
if (target == null) {
return false;
}
this.targetX = target.x;
this.targetY = target.y;
this.targetZ = target.z;
return true;
}
@Override
public boolean canContinueToUse() {
return !this.mob.isSleeping() && !this.mob.getNavigation().isDone();
}
@Override
public void start() {
this.mob.getNavigation().moveTo(this.targetX, this.targetY, this.targetZ, this.speed);
}
@Override
public void stop() {
this.mob.getNavigation().stop();
}
private Vec3 pickTarget() {
BlockPos home = this.mob.getHomePos();
int radius = this.mob.getHomeRadius();
double distSqFromHome = this.mob.blockPosition().distSqr(home);
if (distSqFromHome > (double) radius * radius) {
return Vec3.atBottomCenterOf(home);
}
double angle = this.mob.getRandom().nextDouble() * Math.PI * 2.0;
double dist = this.mob.getRandom().nextDouble() * radius;
double x = home.getX() + 0.5 + Math.cos(angle) * dist;
double z = home.getZ() + 0.5 + Math.sin(angle) * dist;
BlockPos top = this.mob.level().getHeightmapPos(Heightmap.Types.MOTION_BLOCKING, new BlockPos((int) x, 0, (int) z));
return new Vec3(x, top.getY(), z);
}
}
@@ -5,80 +5,96 @@ import net.minecraft.core.BlockPos;
import net.minecraft.world.entity.ai.goal.Goal; import net.minecraft.world.entity.ai.goal.Goal;
import net.minecraft.world.level.block.BedBlock; import net.minecraft.world.level.block.BedBlock;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.level.block.state.properties.BedPart;
import java.util.EnumSet; import java.util.EnumSet;
/**
* Ночью Элли ищет ближайшую свободную кровать в пределах домашней зоны (homePos ± homeRadius)
* и идёт к её ИЗНОЖЬЮ. Спит только ночью. После пробуждения встаёт рядом с кроватью (см. wakeUp).
*/
public class EllieSleepGoal extends Goal { public class EllieSleepGoal extends Goal {
private final EllieEntity ellie; private static final int MAX_PATHFIND_ATTEMPTS = 3;
private final int searchRadius; private static final int NO_BED_RETRY_COOLDOWN_TICKS = 200;
private BlockPos bedPos; private static final double REACH_DIST_SQR = 3.0;
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) { private final EllieEntity ellie;
private boolean arrived;
private boolean noBedAvailable;
private int pathAttempts;
private long nextSearchAllowedTime;
public EllieSleepGoal(EllieEntity ellie) {
this.ellie = ellie; this.ellie = ellie;
this.searchRadius = searchRadius; this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
} }
@Override @Override
public boolean canUse() { public boolean canUse() {
if (ellie.isSleeping()) return false; if (ellie.isSleeping()) return false;
if (!ellie.level().isNight() && !ellie.isTired()) return false; if (ellie.getWakeUpCooldown() > 0) return false;
if (!ellie.isNightTime()) return false;
bedPos = findBed(); return ellie.level().getGameTime() >= this.nextSearchAllowedTime;
return bedPos != null;
} }
private BlockPos findBed() { @Override
BlockPos stored = ellie.getBedPos(); public boolean canContinueToUse() {
if (stored != null && isFreeBed(stored) && ellie.distanceToSqr(Vec3.atCenterOf(stored)) < 50 * 50) { return ellie.isNightTime() && !this.noBedAvailable;
return stored;
}
return findNearestBed();
} }
@Override @Override
public void start() { public void start() {
claimed = false; this.arrived = false;
if (bedPos != null) { this.noBedAvailable = false;
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6); this.pathAttempts = 0;
if (!isBedValid(ellie.getBedPos())) {
ellie.setBedPos(findNearbyBedFootPos());
}
if (ellie.getBedPos() != null) {
moveTowardsBed();
} else {
giveUpForNow();
} }
} }
@Override @Override
public void tick() { public void tick() {
if (bedPos == null) return; if (this.arrived) {
ellie.getNavigation().stop();
BlockState currentState = ellie.level().getBlockState(bedPos);
if (!(currentState.getBlock() instanceof BedBlock)) {
bedPos = null;
return; return;
} }
double distSqr = ellie.distanceToSqr(Vec3.atCenterOf(bedPos)); if (this.noBedAvailable) {
return;
}
if (distSqr < REACH_DIST_SQR) { BlockPos bed = ellie.getBedPos();
ellie.getNavigation().stop(); if (!isBedValid(bed)) {
BlockPos replacement = findNearbyBedFootPos();
ellie.setBedPos(replacement);
if (replacement == null) {
giveUpForNow();
return;
}
moveTowardsBed();
return;
}
if (!claimed) { if (ellie.blockPosition().distSqr(bed) <= REACH_DIST_SQR) {
claimed = ellie.occupyBed(bedPos); claimBed(bed);
} return;
}
if (claimed && !ellie.isSleeping()) { if (ellie.getNavigation().isDone()) {
ellie.setBedSleepPos(bedPos); this.pathAttempts++;
} if (this.pathAttempts > MAX_PATHFIND_ATTEMPTS) {
ellie.setBedPos(null);
if (ellie.isSleeping()) { giveUpForNow();
sleepTimer++; return;
}
} else {
if (ellie.getNavigation().isDone()) {
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
} }
moveTowardsBed();
} }
} }
@@ -87,59 +103,70 @@ public class EllieSleepGoal extends Goal {
if (ellie.isSleeping()) { if (ellie.isSleeping()) {
ellie.wakeUp(); ellie.wakeUp();
} }
sleepTimer = 0; ellie.getNavigation().stop();
bedPos = null; this.arrived = false;
claimed = false; this.noBedAvailable = false;
} }
@Override private void giveUpForNow() {
public boolean canContinueToUse() { this.noBedAvailable = true;
if (bedPos != null) { this.nextSearchAllowedTime = ellie.level().getGameTime() + NO_BED_RETRY_COOLDOWN_TICKS;
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() { private void moveTowardsBed() {
BlockPos entityPos = ellie.blockPosition(); BlockPos bed = ellie.getBedPos();
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos(); ellie.getNavigation().moveTo(bed.getX() + 0.5, bed.getY(), bed.getZ() + 0.5, 1.0);
BlockPos nearest = null; }
double nearestDist = Double.MAX_VALUE;
for (int x = -searchRadius; x <= searchRadius; x++) { private void claimBed(BlockPos footPos) {
for (int z = -searchRadius; z <= searchRadius; z++) { this.arrived = true;
for (int y = -2; y <= 2; y++) { ellie.getNavigation().stop();
mutable.set(entityPos.getX() + x, entityPos.getY() + y, entityPos.getZ() + z);
if (isFreeBed(mutable)) { if (ellie.occupyBed(footPos)) {
double dist = entityPos.distSqr(mutable); ellie.setBedSleepPos(footPos);
if (dist < nearestDist) { ellie.markSleptTonight();
nearestDist = dist; } else {
nearest = mutable.immutable(); this.arrived = false;
} ellie.setBedPos(null);
} giveUpForNow();
} }
}
private boolean isBedValid(BlockPos pos) {
if (pos == null) {
return false;
}
return ellie.level().getBlockState(pos).getBlock() instanceof BedBlock;
}
private BlockPos findNearbyBedFootPos() {
BlockPos home = ellie.getHomePos();
int radius = ellie.getHomeRadius();
BlockPos ownPos = ellie.blockPosition();
BlockPos min = home.offset(-radius, -4, -radius);
BlockPos max = home.offset(radius, 4, radius);
BlockPos best = null;
double bestDistSq = Double.MAX_VALUE;
for (BlockPos pos : BlockPos.betweenClosed(min, max)) {
BlockState state = ellie.level().getBlockState(pos);
if (!(state.getBlock() instanceof BedBlock) || state.getValue(BedBlock.OCCUPIED)) {
continue;
}
BedPart part = state.getValue(BedBlock.PART);
BlockPos footPos = (part == BedPart.FOOT)
? pos
: pos.relative(state.getValue(BedBlock.FACING).getOpposite());
double distSq = ownPos.distSqr(footPos);
if (distSq < bestDistSq) {
bestDistSq = distSq;
best = footPos.immutable();
} }
} }
return nearest; return best;
}
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;
} }
} }
@@ -18,6 +18,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*; import net.minecraft.world.entity.*;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@@ -91,15 +92,28 @@ public class EllieEntity extends Animal implements GeoEntity {
private float prevYRot; private float prevYRot;
private float yawDeltaAccumulator; private float yawDeltaAccumulator;
private BlockPos bedPos; private BlockPos bedPos;
private Direction lastBedDir;
private double lastX, lastZ; private double lastX, lastZ;
private int crouchTimer; private int crouchTimer;
private int debugAnimTimer; private int debugAnimTimer;
private boolean needsSleepRestore; private boolean needsSleepRestore;
private int wakeUpCooldown;
private BlockPos homePos;
private int homeRadius = 20;
private java.util.UUID ownerUuid;
private int missedNights;
private boolean sleptTonight;
private boolean nightCheckedToday;
private boolean homeCheckedToday;
private static final int SLEEPY_MISSED_NIGHTS_THRESHOLD = 2;
private static final long MORNING_CHECK_WINDOW = 20L;
public EllieEntity(EntityType<? extends Animal> type, Level level) { public EllieEntity(EntityType<? extends Animal> type, Level level) {
super(type, level); super(type, level);
((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true); ((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
this.setPersistenceRequired(); this.setPersistenceRequired();
this.homePos = this.blockPosition();
lastX = getX(); lastX = getX();
lastZ = getZ(); lastZ = getZ();
prevYRot = getYRot(); prevYRot = getYRot();
@@ -138,11 +152,12 @@ public class EllieEntity extends Animal implements GeoEntity {
@Override @Override
protected void registerGoals() { protected void registerGoals() {
BasicAI.addBasicGoals(this, this.goalSelector, 4); BasicAI.addBasicGoals(this, this.goalSelector, 4);
this.goalSelector.addGoal(1, new EllieSleepGoal(this, 30)); this.goalSelector.addGoal(1, new EllieSleepGoal(this));
this.goalSelector.addGoal(1, new EllieDoorInteractGoal(this)); this.goalSelector.addGoal(1, new EllieDoorInteractGoal(this));
this.goalSelector.addGoal(1, new IdleAnimationGoal(this)); this.goalSelector.addGoal(1, new IdleAnimationGoal(this));
this.goalSelector.addGoal(2, new ShelterGoal(this)); this.goalSelector.addGoal(2, new ShelterGoal(this));
this.goalSelector.addGoal(3, new me.sashegdev.fabled_hearts.ai.FollowPlayerGoal(this)); this.goalSelector.addGoal(3, new me.sashegdev.fabled_hearts.ai.FollowPlayerGoal(this));
this.goalSelector.addGoal(4, new me.sashegdev.fabled_hearts.ai.BoundedWanderGoal(this, 0.8));
} }
@Override @Override
@@ -183,10 +198,16 @@ public class EllieEntity extends Animal implements GeoEntity {
} }
} }
if (wakeUpCooldown > 0) wakeUpCooldown--;
tickNightlySleepTracking();
tickHomeFollowsOwnerSpawn();
tickHomeBounds();
boolean isNight = level().isNight(); boolean isNight = level().isNight();
boolean raining = level().isRainingAt(blockPosition()); boolean raining = level().isRainingAt(blockPosition());
boolean lowCeiling = checkLowCeiling(); boolean lowCeiling = checkLowCeiling();
boolean tired = ticksWithoutSleep > 48000; boolean tired = missedNights >= SLEEPY_MISSED_NIGHTS_THRESHOLD;
boolean sleeping = entityData.get(DATA_SLEEPING); boolean sleeping = entityData.get(DATA_SLEEPING);
boolean falling = !onGround() && getDeltaMovement().y < -0.1; boolean falling = !onGround() && getDeltaMovement().y < -0.1;
boolean moving = isMoving(); boolean moving = isMoving();
@@ -213,6 +234,28 @@ public class EllieEntity extends Animal implements GeoEntity {
if (sleeping) { if (sleeping) {
setPose(Pose.SLEEPING); setPose(Pose.SLEEPING);
crouchTimer = 0; crouchTimer = 0;
if (bedPos != null && distanceToSqr(Vec3.atCenterOf(bedPos)) > 9.0) {
wakeUp();
}
Direction bedDir = getBedOrientation();
if (bedDir != null) {
BlockState bedState = level().getBlockState(bedPos);
BlockPos footPos = bedState.getValue(BedBlock.PART) == BedPart.HEAD
? bedPos.relative(bedDir.getOpposite())
: bedPos;
setPos(footPos.getX() + 0.5, bedPos.getY(), footPos.getZ() + 0.5);
setDeltaMovement(Vec3.ZERO);
float yaw = bedDir.toYRot();
setYRot(yaw);
yRotO = yaw;
yBodyRot = yaw;
yBodyRotO = yaw;
yHeadRot = yaw;
yHeadRotO = yaw;
setXRot(0);
xRotO = 0;
}
} else if (lowCeiling) { } else if (lowCeiling) {
if (onGround() || wasCrouching) { if (onGround() || wasCrouching) {
setPose(Pose.CROUCHING); setPose(Pose.CROUCHING);
@@ -232,8 +275,8 @@ public class EllieEntity extends Animal implements GeoEntity {
for (Player player : level().players()) { for (Player player : level().players()) {
if (player.distanceToSqr(this) < 64 * 64) { if (player.distanceToSqr(this) < 64 * 64) {
ModNetworking.CHANNEL.send( ModNetworking.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> (ServerPlayer) player), PacketDistributor.PLAYER.with(() -> (ServerPlayer) player),
new DebugSyncPacket(this.getId(), wps)); new DebugSyncPacket(this.getId(), wps, getDebugBrainString()));
} }
} }
} }
@@ -287,9 +330,10 @@ public class EllieEntity extends Animal implements GeoEntity {
.map(g -> g.getGoal().getClass().getSimpleName()) .map(g -> g.getGoal().getClass().getSimpleName())
.orElse("none"); .orElse("none");
sb.append("Goal:").append(goalName).append("|"); sb.append("Goal:").append(goalName).append("|");
sb.append("Home:").append(homePos != null ? homePos : "-").append("|");
sb.append("Bed:").append(bedPos != null ? bedPos : "-").append("|"); sb.append("Bed:").append(bedPos != null ? bedPos : "-").append("|");
sb.append("Sleep:").append(entityData.get(DATA_SLEEPING)).append("|"); sb.append("Sleep:").append(entityData.get(DATA_SLEEPING)).append("|");
sb.append("Tired:").append(ticksWithoutSleep).append("|"); sb.append("Missed:").append(missedNights).append("|");
sb.append("Move:").append(entityData.get(DATA_MOVING)).append("|"); sb.append("Move:").append(entityData.get(DATA_MOVING)).append("|");
sb.append("Pose:").append(getPose()).append("|"); sb.append("Pose:").append(getPose()).append("|");
sb.append("LowCeil:").append(entityData.get(DATA_UNDER_LOW_CEILING)).append("|"); sb.append("LowCeil:").append(entityData.get(DATA_UNDER_LOW_CEILING)).append("|");
@@ -304,6 +348,100 @@ public class EllieEntity extends Animal implements GeoEntity {
entityData.set(DATA_DEBUG_BRAIN, sb.toString()); entityData.set(DATA_DEBUG_BRAIN, sb.toString());
} }
public boolean isNightTime() {
long time = level().getDayTime() % 24000L;
return time >= 13000L && time <= 23000L;
}
public BlockPos getHomePos() {
return homePos == null ? blockPosition() : homePos;
}
public void setHomePos(BlockPos pos) {
this.homePos = pos;
}
public int getHomeRadius() {
return homeRadius;
}
public void setHomeRadius(int radius) {
this.homeRadius = radius;
}
public void setOwnerUuid(java.util.UUID uuid) {
this.ownerUuid = uuid;
}
public java.util.UUID getOwnerUuid() {
return ownerUuid;
}
public void markSleptTonight() {
this.sleptTonight = true;
}
private void tickHomeFollowsOwnerSpawn() {
long timeOfDay = level().getDayTime() % 24000L;
if (timeOfDay > MORNING_CHECK_WINDOW) {
this.homeCheckedToday = false;
return;
}
if (this.homeCheckedToday || ownerUuid == null) {
return;
}
this.homeCheckedToday = true;
if (!(level() instanceof ServerLevel serverLevel)) {
return;
}
ServerPlayer owner = serverLevel.getServer().getPlayerList().getPlayer(ownerUuid);
if (owner == null) {
return;
}
BlockPos ownerSpawn = owner.getRespawnPosition();
if (ownerSpawn == null) {
return;
}
if (ownerSpawn.distSqr(getHomePos()) > 4.0) {
this.setHomePos(ownerSpawn);
this.forgetBed();
}
}
private void tickNightlySleepTracking() {
long timeOfDay = level().getDayTime() % 24000L;
if (timeOfDay > MORNING_CHECK_WINDOW) {
this.nightCheckedToday = false;
return;
}
if (this.nightCheckedToday) {
return;
}
this.nightCheckedToday = true;
if (this.sleptTonight) {
this.missedNights = 0;
} else {
this.missedNights++;
}
this.sleptTonight = false;
entityData.set(DATA_TIRED, this.missedNights >= SLEEPY_MISSED_NIGHTS_THRESHOLD);
}
private void tickHomeBounds() {
if (isSleeping()) {
return;
}
BlockPos home = getHomePos();
double distSq = blockPosition().distSqr(home);
double maxSq = (double) homeRadius * homeRadius;
if (distSq > maxSq * 1.1 && navigation.isDone()) {
navigation.moveTo(home.getX() + 0.5, home.getY(), home.getZ() + 0.5, 1.0);
}
}
public void toggleDebug() { public void toggleDebug() {
entityData.set(DATA_DEBUG, !entityData.get(DATA_DEBUG)); entityData.set(DATA_DEBUG, !entityData.get(DATA_DEBUG));
if (!entityData.get(DATA_DEBUG)) { if (!entityData.get(DATA_DEBUG)) {
@@ -344,7 +482,7 @@ public class EllieEntity extends Animal implements GeoEntity {
@Override @Override
public EntityDimensions getDimensions(Pose pose) { public EntityDimensions getDimensions(Pose pose) {
if (pose == Pose.SLEEPING) return EntityDimensions.fixed(0.15f, 0.15f); if (pose == Pose.SLEEPING) return EntityDimensions.fixed(0.2f, 0.2f);
if (pose == Pose.CROUCHING) return EntityDimensions.fixed(0.6f, 1.0f); if (pose == Pose.CROUCHING) return EntityDimensions.fixed(0.6f, 1.0f);
return super.getDimensions(pose); return super.getDimensions(pose);
} }
@@ -418,27 +556,80 @@ public class EllieEntity extends Animal implements GeoEntity {
if (!(state.getBlock() instanceof BedBlock)) return; if (!(state.getBlock() instanceof BedBlock)) return;
Direction facing = state.getValue(BedBlock.FACING); Direction facing = state.getValue(BedBlock.FACING);
BlockPos headPos = state.getValue(BedBlock.PART) == BedPart.HEAD BlockPos footPos = state.getValue(BedBlock.PART) == BedPart.HEAD
? pos ? pos.relative(facing.getOpposite())
: pos.relative(facing); : pos;
setPose(Pose.SLEEPING); setPose(Pose.SLEEPING);
setPos(headPos.getX() + 0.5, pos.getY() + 0.6875, headPos.getZ() + 0.5); setPos(footPos.getX() + 0.5, footPos.getY(), footPos.getZ() + 0.5);
setSleepingPos(headPos); float yaw = facing.toYRot();
float yRot = facing.toYRot(); setYRot(yaw);
setYRot(yRot); yRotO = yaw;
yRotO = yRot; yBodyRot = yaw;
setYHeadRot(yRot); yBodyRotO = yaw;
yHeadRot = yaw;
yHeadRotO = yaw;
setXRot(0);
xRotO = 0;
lastBedDir = facing;
setSleeping(true); setSleeping(true);
} }
@Override
public Direction getBedOrientation() {
if (bedPos != null && isSleeping()) {
BlockState state = level().getBlockState(bedPos);
if (state.getBlock() instanceof BedBlock) {
return state.getValue(BedBlock.FACING);
}
}
return null;
}
public Direction getLastBedDir() { return lastBedDir; }
public void wakeUp() { public void wakeUp() {
BlockPos foot = bedPos;
Direction dir = lastBedDir;
releaseBed(); releaseBed();
forgetBed(); forgetBed();
setSleeping(false); setSleeping(false);
setPose(Pose.STANDING); setPose(Pose.STANDING);
navigation.stop(); navigation.stop();
if (foot != null && dir != null && !level().isClientSide) {
BlockPos stand = foot.relative(dir.getOpposite());
BlockState standState = level().getBlockState(stand);
boolean safeSpot = !standState.blocksMotion() && level().getBlockState(stand.above()).isAir();
if (safeSpot) {
setPos(stand.getX() + 0.5, stand.getY(), stand.getZ() + 0.5);
float yaw = dir.toYRot();
setYRot(yaw);
yRotO = yaw;
yBodyRot = yaw;
yBodyRotO = yaw;
yHeadRot = yaw;
yHeadRotO = yaw;
setXRot(0);
xRotO = 0;
wakeUpCooldown = 200;
return;
}
}
setDeltaMovement(getDeltaMovement().add(0, 0.15, 0)); setDeltaMovement(getDeltaMovement().add(0, 0.15, 0));
wakeUpCooldown = 200;
}
@Override
public boolean isPushable() {
return !isSleeping() && super.isPushable();
}
@Override
public void push(Entity entity) {
if (isSleeping()) {
return;
}
super.push(entity);
} }
@Override @Override
@@ -561,9 +752,6 @@ public class EllieEntity extends Animal implements GeoEntity {
return InteractionResult.SUCCESS; return InteractionResult.SUCCESS;
} }
if (entityData.get(DATA_TIRED)) {
return InteractionResult.SUCCESS;
}
if (this.level().isClientSide) { if (this.level().isClientSide) {
ModNetworking.CHANNEL.sendToServer(new OpenDialogPacket(this.getId())); ModNetworking.CHANNEL.sendToServer(new OpenDialogPacket(this.getId()));
return InteractionResult.SUCCESS; return InteractionResult.SUCCESS;
@@ -586,6 +774,9 @@ public class EllieEntity extends Animal implements GeoEntity {
return false; return false;
} }
} }
if (isSleeping() && !level().isClientSide) {
wakeUp();
}
return super.hurt(source, amount); return super.hurt(source, amount);
} }
@@ -595,6 +786,16 @@ public class EllieEntity extends Animal implements GeoEntity {
tag.putInt("TicksWithoutSleep", ticksWithoutSleep); tag.putInt("TicksWithoutSleep", ticksWithoutSleep);
tag.putFloat("Relationship", relationshipPoints); tag.putFloat("Relationship", relationshipPoints);
if (bedPos != null) tag.putLong("BedPos", bedPos.asLong()); if (bedPos != null) tag.putLong("BedPos", bedPos.asLong());
if (homePos != null) {
tag.putInt("HomeX", homePos.getX());
tag.putInt("HomeY", homePos.getY());
tag.putInt("HomeZ", homePos.getZ());
}
tag.putInt("HomeRadius", homeRadius);
tag.putInt("MissedNights", missedNights);
if (ownerUuid != null) {
tag.putUUID("OwnerUuid", ownerUuid);
}
} }
@Override @Override
@@ -608,6 +809,16 @@ public class EllieEntity extends Animal implements GeoEntity {
needsSleepRestore = true; needsSleepRestore = true;
} }
} }
if (tag.contains("HomeX")) {
homePos = new BlockPos(tag.getInt("HomeX"), tag.getInt("HomeY"), tag.getInt("HomeZ"));
}
if (tag.contains("HomeRadius")) {
homeRadius = tag.getInt("HomeRadius");
}
missedNights = tag.getInt("MissedNights");
if (tag.contains("OwnerUuid")) {
ownerUuid = tag.getUUID("OwnerUuid");
}
} }
@Override @Override
@@ -627,6 +838,7 @@ public class EllieEntity extends Animal implements GeoEntity {
this.relationshipPoints = Math.min(100, Math.max(0, this.relationshipPoints + amount)); this.relationshipPoints = Math.min(100, Math.max(0, this.relationshipPoints + amount));
} }
public int getTicksWithoutSleep() { return ticksWithoutSleep; } public int getTicksWithoutSleep() { return ticksWithoutSleep; }
public int getWakeUpCooldown() { return wakeUpCooldown; }
public int getFollowTargetId() { return entityData.get(DATA_FOLLOW_TARGET); } public int getFollowTargetId() { return entityData.get(DATA_FOLLOW_TARGET); }
public boolean isFollowingPlayer() { return entityData.get(DATA_FOLLOW_TARGET) > 0; } public boolean isFollowingPlayer() { return entityData.get(DATA_FOLLOW_TARGET) > 0; }
public int getDebugAnim() { return entityData.get(DATA_DEBUG_ANIM); } public int getDebugAnim() { return entityData.get(DATA_DEBUG_ANIM); }
@@ -2,13 +2,11 @@ package me.sashegdev.fabled_hearts.entity.ellie;
import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font; import net.minecraft.client.gui.Font;
import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.Pose;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -21,25 +19,6 @@ public class EllieRenderer extends GeoEntityRenderer<EllieEntity> {
this.shadowRadius = 0.5f; this.shadowRadius = 0.5f;
} }
@Override
protected void applyRotations(EllieEntity entity, PoseStack poseStack, float tick, float yRot, float partialTick) {
if (entity.getPose() == Pose.SLEEPING) {
Direction bedDir = entity.getBedOrientation();
if (bedDir != null) {
poseStack.translate(
-bedDir.getStepX() * 1.847,
0.0F,
-bedDir.getStepZ() * 1.847
);
poseStack.mulPose(Axis.YP.rotationDegrees(bedDir.toYRot()));
poseStack.mulPose(Axis.XP.rotationDegrees(90.0F));
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
}
return;
}
super.applyRotations(entity, poseStack, tick, yRot, partialTick);
}
@Override @Override
public RenderType getRenderType(EllieEntity animatable, ResourceLocation texture, public RenderType getRenderType(EllieEntity animatable, ResourceLocation texture,
@Nullable MultiBufferSource bufferSource, float partialTick) { @Nullable MultiBufferSource bufferSource, float partialTick) {
@@ -50,7 +29,6 @@ public class EllieRenderer extends GeoEntityRenderer<EllieEntity> {
public void render(EllieEntity entity, float entityYaw, float partialTick, PoseStack poseStack, public void render(EllieEntity entity, float entityYaw, float partialTick, PoseStack poseStack,
MultiBufferSource bufferSource, int packedLight) { MultiBufferSource bufferSource, int packedLight) {
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight); super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
if (entity.isDebugMode()) { if (entity.isDebugMode()) {
renderBrainText(entity, poseStack, bufferSource, packedLight); renderBrainText(entity, poseStack, bufferSource, packedLight);
} }
@@ -40,6 +40,9 @@ public class EllieSpawnItem extends Item {
var entity = ModEntities.ELLIE.get().create(serverLevel); var entity = ModEntities.ELLIE.get().create(serverLevel);
if (entity != null) { if (entity != null) {
entity.setPos(player.getX(), player.getY(), player.getZ()); entity.setPos(player.getX(), player.getY(), player.getZ());
entity.setHomePos(entity.blockPosition());
entity.setHomeRadius(20);
entity.setOwnerUuid(player.getUUID());
serverLevel.addFreshEntity(entity); serverLevel.addFreshEntity(entity);
data.setEllieUUID(entity.getUUID()); data.setEllieUUID(entity.getUUID());
player.sendSystemMessage(Component.literal("§aEllie появилась!")); player.sendSystemMessage(Component.literal("§aEllie появилась!"));
@@ -13897,6 +13897,14 @@
"loop": true, "loop": true,
"animation_length": 2.25, "animation_length": 2.25,
"bones": { "bones": {
"root": {
"rotation": {
"vector": [90, 0, -180]
},
"position": {
"vector": [0, 9.1, 9]
}
},
"torso": { "torso": {
"rotation": { "rotation": {
"0.0833": { "0.0833": {