Порт из 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/
runs/
# IDE build output
bin/
# Forge Gradle
crash-reports/
logs/
Vendored Regular → Executable
View File
@@ -11,7 +11,6 @@ public class BasicAI {
selector.addGoal(p++, new FloatGoal(mob));
selector.addGoal(p++, new LookAtPlayerGoal(mob, Player.class, 8.0f));
selector.addGoal(p++, new RandomLookAroundGoal(mob));
selector.addGoal(p++, new WaterAvoidingRandomStrollGoal(mob, 0.8));
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.level.block.BedBlock;
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;
/**
* Ночью Элли ищет ближайшую свободную кровать в пределах домашней зоны (homePos ± homeRadius)
* и идёт к её ИЗНОЖЬЮ. Спит только ночью. После пробуждения встаёт рядом с кроватью (см. wakeUp).
*/
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;
private static final int MAX_PATHFIND_ATTEMPTS = 3;
private static final int NO_BED_RETRY_COOLDOWN_TICKS = 200;
private static final double REACH_DIST_SQR = 3.0;
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.searchRadius = searchRadius;
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
}
@Override
public boolean canUse() {
if (ellie.isSleeping()) return false;
if (!ellie.level().isNight() && !ellie.isTired()) return false;
bedPos = findBed();
return bedPos != null;
if (ellie.getWakeUpCooldown() > 0) return false;
if (!ellie.isNightTime()) return false;
return ellie.level().getGameTime() >= this.nextSearchAllowedTime;
}
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 boolean canContinueToUse() {
return ellie.isNightTime() && !this.noBedAvailable;
}
@Override
public void start() {
claimed = false;
if (bedPos != null) {
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
this.arrived = false;
this.noBedAvailable = false;
this.pathAttempts = 0;
if (!isBedValid(ellie.getBedPos())) {
ellie.setBedPos(findNearbyBedFootPos());
}
if (ellie.getBedPos() != null) {
moveTowardsBed();
} else {
giveUpForNow();
}
}
@Override
public void tick() {
if (bedPos == null) return;
BlockState currentState = ellie.level().getBlockState(bedPos);
if (!(currentState.getBlock() instanceof BedBlock)) {
bedPos = null;
if (this.arrived) {
ellie.getNavigation().stop();
return;
}
double distSqr = ellie.distanceToSqr(Vec3.atCenterOf(bedPos));
if (this.noBedAvailable) {
return;
}
if (distSqr < REACH_DIST_SQR) {
ellie.getNavigation().stop();
BlockPos bed = ellie.getBedPos();
if (!isBedValid(bed)) {
BlockPos replacement = findNearbyBedFootPos();
ellie.setBedPos(replacement);
if (replacement == null) {
giveUpForNow();
return;
}
moveTowardsBed();
return;
}
if (!claimed) {
claimed = ellie.occupyBed(bedPos);
}
if (ellie.blockPosition().distSqr(bed) <= REACH_DIST_SQR) {
claimBed(bed);
return;
}
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);
if (ellie.getNavigation().isDone()) {
this.pathAttempts++;
if (this.pathAttempts > MAX_PATHFIND_ATTEMPTS) {
ellie.setBedPos(null);
giveUpForNow();
return;
}
moveTowardsBed();
}
}
@@ -87,59 +103,70 @@ public class EllieSleepGoal extends Goal {
if (ellie.isSleeping()) {
ellie.wakeUp();
}
sleepTimer = 0;
bedPos = null;
claimed = false;
ellie.getNavigation().stop();
this.arrived = false;
this.noBedAvailable = 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 void giveUpForNow() {
this.noBedAvailable = true;
this.nextSearchAllowedTime = ellie.level().getGameTime() + NO_BED_RETRY_COOLDOWN_TICKS;
}
private BlockPos findNearestBed() {
BlockPos entityPos = ellie.blockPosition();
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
BlockPos nearest = null;
double nearestDist = Double.MAX_VALUE;
private void moveTowardsBed() {
BlockPos bed = ellie.getBedPos();
ellie.getNavigation().moveTo(bed.getX() + 0.5, bed.getY(), bed.getZ() + 0.5, 1.0);
}
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();
}
}
}
private void claimBed(BlockPos footPos) {
this.arrived = true;
ellie.getNavigation().stop();
if (ellie.occupyBed(footPos)) {
ellie.setBedSleepPos(footPos);
ellie.markSleptTonight();
} else {
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;
}
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;
return best;
}
}
@@ -18,6 +18,7 @@ import java.util.ArrayList;
import java.util.List;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.*;
import net.minecraft.world.item.ItemStack;
@@ -91,15 +92,28 @@ public class EllieEntity extends Animal implements GeoEntity {
private float prevYRot;
private float yawDeltaAccumulator;
private BlockPos bedPos;
private Direction lastBedDir;
private double lastX, lastZ;
private int crouchTimer;
private int debugAnimTimer;
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) {
super(type, level);
((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
this.setPersistenceRequired();
this.homePos = this.blockPosition();
lastX = getX();
lastZ = getZ();
prevYRot = getYRot();
@@ -138,11 +152,12 @@ public class EllieEntity extends Animal implements GeoEntity {
@Override
protected void registerGoals() {
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 IdleAnimationGoal(this));
this.goalSelector.addGoal(2, new ShelterGoal(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
@@ -183,10 +198,16 @@ public class EllieEntity extends Animal implements GeoEntity {
}
}
if (wakeUpCooldown > 0) wakeUpCooldown--;
tickNightlySleepTracking();
tickHomeFollowsOwnerSpawn();
tickHomeBounds();
boolean isNight = level().isNight();
boolean raining = level().isRainingAt(blockPosition());
boolean lowCeiling = checkLowCeiling();
boolean tired = ticksWithoutSleep > 48000;
boolean tired = missedNights >= SLEEPY_MISSED_NIGHTS_THRESHOLD;
boolean sleeping = entityData.get(DATA_SLEEPING);
boolean falling = !onGround() && getDeltaMovement().y < -0.1;
boolean moving = isMoving();
@@ -213,6 +234,28 @@ public class EllieEntity extends Animal implements GeoEntity {
if (sleeping) {
setPose(Pose.SLEEPING);
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) {
if (onGround() || wasCrouching) {
setPose(Pose.CROUCHING);
@@ -232,8 +275,8 @@ public class EllieEntity extends Animal implements GeoEntity {
for (Player player : level().players()) {
if (player.distanceToSqr(this) < 64 * 64) {
ModNetworking.CHANNEL.send(
PacketDistributor.PLAYER.with(() -> (ServerPlayer) player),
new DebugSyncPacket(this.getId(), wps));
PacketDistributor.PLAYER.with(() -> (ServerPlayer) player),
new DebugSyncPacket(this.getId(), wps, getDebugBrainString()));
}
}
}
@@ -287,9 +330,10 @@ public class EllieEntity extends Animal implements GeoEntity {
.map(g -> g.getGoal().getClass().getSimpleName())
.orElse("none");
sb.append("Goal:").append(goalName).append("|");
sb.append("Home:").append(homePos != null ? homePos : "-").append("|");
sb.append("Bed:").append(bedPos != null ? bedPos : "-").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("Pose:").append(getPose()).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());
}
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() {
entityData.set(DATA_DEBUG, !entityData.get(DATA_DEBUG));
if (!entityData.get(DATA_DEBUG)) {
@@ -344,7 +482,7 @@ public class EllieEntity extends Animal implements GeoEntity {
@Override
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);
return super.getDimensions(pose);
}
@@ -418,27 +556,80 @@ public class EllieEntity extends Animal implements GeoEntity {
if (!(state.getBlock() instanceof BedBlock)) return;
Direction facing = state.getValue(BedBlock.FACING);
BlockPos headPos = state.getValue(BedBlock.PART) == BedPart.HEAD
? pos
: pos.relative(facing);
BlockPos footPos = state.getValue(BedBlock.PART) == BedPart.HEAD
? pos.relative(facing.getOpposite())
: pos;
setPose(Pose.SLEEPING);
setPos(headPos.getX() + 0.5, pos.getY() + 0.6875, headPos.getZ() + 0.5);
setSleepingPos(headPos);
float yRot = facing.toYRot();
setYRot(yRot);
yRotO = yRot;
setYHeadRot(yRot);
setPos(footPos.getX() + 0.5, footPos.getY(), footPos.getZ() + 0.5);
float yaw = facing.toYRot();
setYRot(yaw);
yRotO = yaw;
yBodyRot = yaw;
yBodyRotO = yaw;
yHeadRot = yaw;
yHeadRotO = yaw;
setXRot(0);
xRotO = 0;
lastBedDir = facing;
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() {
BlockPos foot = bedPos;
Direction dir = lastBedDir;
releaseBed();
forgetBed();
setSleeping(false);
setPose(Pose.STANDING);
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));
wakeUpCooldown = 200;
}
@Override
public boolean isPushable() {
return !isSleeping() && super.isPushable();
}
@Override
public void push(Entity entity) {
if (isSleeping()) {
return;
}
super.push(entity);
}
@Override
@@ -561,9 +752,6 @@ public class EllieEntity extends Animal implements GeoEntity {
return InteractionResult.SUCCESS;
}
if (entityData.get(DATA_TIRED)) {
return InteractionResult.SUCCESS;
}
if (this.level().isClientSide) {
ModNetworking.CHANNEL.sendToServer(new OpenDialogPacket(this.getId()));
return InteractionResult.SUCCESS;
@@ -586,6 +774,9 @@ public class EllieEntity extends Animal implements GeoEntity {
return false;
}
}
if (isSleeping() && !level().isClientSide) {
wakeUp();
}
return super.hurt(source, amount);
}
@@ -595,6 +786,16 @@ public class EllieEntity extends Animal implements GeoEntity {
tag.putInt("TicksWithoutSleep", ticksWithoutSleep);
tag.putFloat("Relationship", relationshipPoints);
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
@@ -608,6 +809,16 @@ public class EllieEntity extends Animal implements GeoEntity {
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
@@ -627,6 +838,7 @@ public class EllieEntity extends Animal implements GeoEntity {
this.relationshipPoints = Math.min(100, Math.max(0, this.relationshipPoints + amount));
}
public int getTicksWithoutSleep() { return ticksWithoutSleep; }
public int getWakeUpCooldown() { return wakeUpCooldown; }
public int getFollowTargetId() { return entityData.get(DATA_FOLLOW_TARGET); }
public boolean isFollowingPlayer() { return entityData.get(DATA_FOLLOW_TARGET) > 0; }
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.VertexConsumer;
import com.mojang.math.Axis;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Font;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Pose;
import org.jetbrains.annotations.Nullable;
@@ -21,25 +19,6 @@ public class EllieRenderer extends GeoEntityRenderer<EllieEntity> {
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
public RenderType getRenderType(EllieEntity animatable, ResourceLocation texture,
@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,
MultiBufferSource bufferSource, int packedLight) {
super.render(entity, entityYaw, partialTick, poseStack, bufferSource, packedLight);
if (entity.isDebugMode()) {
renderBrainText(entity, poseStack, bufferSource, packedLight);
}
@@ -40,6 +40,9 @@ public class EllieSpawnItem extends Item {
var entity = ModEntities.ELLIE.get().create(serverLevel);
if (entity != null) {
entity.setPos(player.getX(), player.getY(), player.getZ());
entity.setHomePos(entity.blockPosition());
entity.setHomeRadius(20);
entity.setOwnerUuid(player.getUUID());
serverLevel.addFreshEntity(entity);
data.setEllieUUID(entity.getUUID());
player.sendSystemMessage(Component.literal("§aEllie появилась!"));
@@ -13897,6 +13897,14 @@
"loop": true,
"animation_length": 2.25,
"bones": {
"root": {
"rotation": {
"vector": [90, 0, -180]
},
"position": {
"vector": [0, 9.1, 9]
}
},
"torso": {
"rotation": {
"0.0833": {