Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8a1bd4bc44 | |||
| c51a6081b5 |
@@ -115,9 +115,6 @@ gradle-app.setting
|
||||
run/
|
||||
runs/
|
||||
|
||||
# IDE build output
|
||||
bin/
|
||||
|
||||
# Forge Gradle
|
||||
crash-reports/
|
||||
logs/
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
--- a/net/minecraft/client/renderer/entity/LivingEntityRenderer.java
|
||||
+++ b/net/minecraft/client/renderer/entity/LivingEntityRenderer.java
|
||||
@@ -49,14 +_,17 @@
|
||||
}
|
||||
|
||||
public void m_7392_(T p_115308_, float p_115309_, float p_115310_, PoseStack p_115311_, MultiBufferSource p_115312_, int p_115313_) {
|
||||
+ if (net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.RenderLivingEvent.Pre<T, M>(p_115308_, this, p_115310_, p_115311_, p_115312_, p_115313_))) return;
|
||||
p_115311_.m_85836_();
|
||||
this.f_115290_.f_102608_ = this.m_115342_(p_115308_, p_115310_);
|
||||
- this.f_115290_.f_102609_ = p_115308_.m_20159_();
|
||||
+
|
||||
+ boolean shouldSit = p_115308_.m_20159_() && (p_115308_.m_20202_() != null && p_115308_.m_20202_().shouldRiderSit());
|
||||
+ this.f_115290_.f_102609_ = shouldSit;
|
||||
this.f_115290_.f_102610_ = p_115308_.m_6162_();
|
||||
float f = Mth.m_14189_(p_115310_, p_115308_.f_20884_, p_115308_.f_20883_);
|
||||
float f1 = Mth.m_14189_(p_115310_, p_115308_.f_20886_, p_115308_.f_20885_);
|
||||
float f2 = f1 - f;
|
||||
- if (p_115308_.m_20159_() && p_115308_.m_20202_() instanceof LivingEntity) {
|
||||
+ if (shouldSit && p_115308_.m_20202_() instanceof LivingEntity) {
|
||||
LivingEntity livingentity = (LivingEntity)p_115308_.m_20202_();
|
||||
f = Mth.m_14189_(p_115310_, livingentity.f_20884_, livingentity.f_20883_);
|
||||
f2 = f1 - f;
|
||||
@@ -98,7 +_,7 @@
|
||||
p_115311_.m_252880_(0.0F, -1.501F, 0.0F);
|
||||
float f8 = 0.0F;
|
||||
float f5 = 0.0F;
|
||||
- if (!p_115308_.m_20159_() && p_115308_.m_6084_()) {
|
||||
+ if (!shouldSit && p_115308_.m_6084_()) {
|
||||
f8 = p_115308_.f_267362_.m_267711_(p_115310_);
|
||||
f5 = p_115308_.f_267362_.m_267590_(p_115310_);
|
||||
if (p_115308_.m_6162_()) {
|
||||
@@ -131,6 +_,7 @@
|
||||
|
||||
p_115311_.m_85849_();
|
||||
super.m_7392_(p_115308_, p_115309_, p_115310_, p_115311_, p_115312_, p_115313_);
|
||||
+ net.minecraftforge.common.MinecraftForge.EVENT_BUS.post(new net.minecraftforge.client.event.RenderLivingEvent.Post<T, M>(p_115308_, this, p_115310_, p_115311_, p_115312_, p_115313_));
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -1,59 +0,0 @@
|
||||
--- a/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
||||
+++ b/net/minecraft/world/level/pathfinder/WalkNodeEvaluator.java
|
||||
@@ -112,7 +_,7 @@
|
||||
BlockPathTypes blockpathtypes = this.m_77567_(this.f_77313_, p_77641_.f_77271_, p_77641_.f_77272_ + 1, p_77641_.f_77273_);
|
||||
BlockPathTypes blockpathtypes1 = this.m_77567_(this.f_77313_, p_77641_.f_77271_, p_77641_.f_77272_, p_77641_.f_77273_);
|
||||
if (this.f_77313_.m_21439_(blockpathtypes) >= 0.0F && blockpathtypes1 != BlockPathTypes.STICKY_HONEY) {
|
||||
- j = Mth.m_14143_(Math.max(1.0F, this.f_77313_.m_274421_()));
|
||||
+ j = Mth.m_14143_(Math.max(1.0F, this.f_77313_.getStepHeight()));
|
||||
}
|
||||
|
||||
double d0 = this.m_142213_(new BlockPos(p_77641_.f_77271_, p_77641_.f_77272_, p_77641_.f_77273_));
|
||||
@@ -306,7 +_,7 @@
|
||||
}
|
||||
|
||||
private double m_255203_() {
|
||||
- return Math.max(1.125D, (double)this.f_77313_.m_274421_());
|
||||
+ return Math.max(1.125D, (double)this.f_77313_.getStepHeight());
|
||||
}
|
||||
|
||||
private Node m_230619_(int p_230620_, int p_230621_, int p_230622_, BlockPathTypes p_230623_, float p_230624_) {
|
||||
@@ -453,6 +_,11 @@
|
||||
if (l != 0 || j1 != 0) {
|
||||
p_77609_.m_122178_(i + l, j + i1, k + j1);
|
||||
BlockState blockstate = p_77608_.m_8055_(p_77609_);
|
||||
+ BlockPathTypes blockPathType = blockstate.getAdjacentBlockPathType(p_77608_, p_77609_, null, p_77610_);
|
||||
+ if (blockPathType != null) return blockPathType;
|
||||
+ FluidState fluidState = blockstate.m_60819_();
|
||||
+ BlockPathTypes fluidPathType = fluidState.getAdjacentBlockPathType(p_77608_, p_77609_, null, p_77610_);
|
||||
+ if (fluidPathType != null) return fluidPathType;
|
||||
if (blockstate.m_60713_(Blocks.f_50128_) || blockstate.m_60713_(Blocks.f_50685_)) {
|
||||
return BlockPathTypes.DANGER_OTHER;
|
||||
}
|
||||
@@ -478,6 +_,8 @@
|
||||
|
||||
protected static BlockPathTypes m_77643_(BlockGetter p_77644_, BlockPos p_77645_) {
|
||||
BlockState blockstate = p_77644_.m_8055_(p_77645_);
|
||||
+ BlockPathTypes type = blockstate.getBlockPathType(p_77644_, p_77645_, null);
|
||||
+ if (type != null) return type;
|
||||
Block block = blockstate.m_60734_();
|
||||
if (blockstate.m_60795_()) {
|
||||
return BlockPathTypes.OPEN;
|
||||
@@ -491,6 +_,8 @@
|
||||
return BlockPathTypes.COCOA;
|
||||
} else if (!blockstate.m_60713_(Blocks.f_50070_) && !blockstate.m_60713_(Blocks.f_152588_)) {
|
||||
FluidState fluidstate = p_77644_.m_6425_(p_77645_);
|
||||
+ BlockPathTypes nonLoggableFluidPathType = fluidstate.getBlockPathType(p_77644_, p_77645_, null, false);
|
||||
+ if (nonLoggableFluidPathType != null) return nonLoggableFluidPathType;
|
||||
if (fluidstate.m_205070_(FluidTags.f_13132_)) {
|
||||
return BlockPathTypes.LAVA;
|
||||
} else if (m_77622_(blockstate)) {
|
||||
@@ -510,6 +_,8 @@
|
||||
if (!blockstate.m_60647_(p_77644_, p_77645_, PathComputationType.LAND)) {
|
||||
return BlockPathTypes.BLOCKED;
|
||||
} else {
|
||||
+ BlockPathTypes loggableFluidPathType = fluidstate.getBlockPathType(p_77644_, p_77645_, null, true);
|
||||
+ if (loggableFluidPathType != null) return loggableFluidPathType;
|
||||
return fluidstate.m_205070_(FluidTags.f_13131_) ? BlockPathTypes.WATER : BlockPathTypes.OPEN;
|
||||
}
|
||||
} else {
|
||||
@@ -1,11 +1,11 @@
|
||||
package me.sashegdev.fabled_hearts;
|
||||
|
||||
import com.mojang.logging.LogUtils;
|
||||
import me.sashegdev.fabled_hearts.client.DebugClientHandler;
|
||||
import me.sashegdev.fabled_hearts.dialog.DialogLoader;
|
||||
import me.sashegdev.fabled_hearts.dialog.DialogManager;
|
||||
import me.sashegdev.fabled_hearts.entity.ellie.EllieEntity;
|
||||
import me.sashegdev.fabled_hearts.entity.ellie.EllieRenderer;
|
||||
import me.sashegdev.fabled_hearts.client.DebugClientHandler;
|
||||
import me.sashegdev.fabled_hearts.network.ModNetworking;
|
||||
import me.sashegdev.fabled_hearts.registry.ModEntities;
|
||||
import me.sashegdev.fabled_hearts.registry.ModItems;
|
||||
@@ -19,7 +19,6 @@ import net.minecraftforge.event.level.BlockEvent;
|
||||
import net.minecraftforge.eventbus.api.IEventBus;
|
||||
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
|
||||
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
|
||||
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
|
||||
import org.slf4j.Logger;
|
||||
@@ -67,13 +66,13 @@ public class Main {
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void onClientSetup(FMLClientSetupEvent event) {
|
||||
DebugClientHandler.init();
|
||||
public static void registerKeyMappings(RegisterKeyMappingsEvent event) {
|
||||
DebugClientHandler.registerKeyMapping(event);
|
||||
}
|
||||
|
||||
@SubscribeEvent
|
||||
public static void registerKeyMappings(RegisterKeyMappingsEvent event) {
|
||||
DebugClientHandler.registerKeyMapping(event);
|
||||
public static void onClientSetup(net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent event) {
|
||||
DebugClientHandler.init();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,10 +88,10 @@ public class Main {
|
||||
var pos = event.getPos();
|
||||
event.getLevel().getEntitiesOfClass(
|
||||
EllieEntity.class,
|
||||
new net.minecraft.world.phys.AABB(pos).inflate(3),
|
||||
e -> e.isBedAtOrAdjacent(pos) || (e.getBedPos() != null && e.getBedPos().equals(pos))
|
||||
new net.minecraft.world.phys.AABB(pos).inflate(2),
|
||||
e -> e.isSleeping() && pos.equals(e.getBedPos())
|
||||
).forEach(e -> {
|
||||
if (e.isSleeping()) e.wakeUp();
|
||||
e.wakeUp();
|
||||
e.forgetBed();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ 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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,84 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -4,97 +4,95 @@ 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.BedBlock;
|
||||
import net.minecraft.world.level.block.DoorBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.block.state.properties.BedPart;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
|
||||
import java.util.EnumSet;
|
||||
|
||||
/**
|
||||
* Ночью Элли ищет ближайшую свободную кровать в пределах домашней зоны (homePos ± homeRadius)
|
||||
* и идёт к её ИЗНОЖЬЮ. Спит только ночью. После пробуждения встаёт рядом с кроватью (см. wakeUp).
|
||||
*/
|
||||
public class EllieSleepGoal extends Goal {
|
||||
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;
|
||||
|
||||
private final EllieEntity ellie;
|
||||
private boolean arrived;
|
||||
private boolean noBedAvailable;
|
||||
private int pathAttempts;
|
||||
private long nextSearchAllowedTime;
|
||||
private final int searchRadius;
|
||||
private BlockPos bedPos;
|
||||
private int sleepTimer;
|
||||
private boolean claimed;
|
||||
private int scanCooldown;
|
||||
private static final int MAX_SLEEP_TICKS = 6000;
|
||||
private static final int SCAN_INTERVAL = 40;
|
||||
private static final double REACH_DIST_SQR = 1.8 * 1.8;
|
||||
|
||||
public EllieSleepGoal(EllieEntity ellie) {
|
||||
public EllieSleepGoal(EllieEntity ellie, int searchRadius) {
|
||||
this.ellie = ellie;
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK, Flag.JUMP));
|
||||
this.searchRadius = searchRadius;
|
||||
this.setFlags(EnumSet.of(Flag.MOVE, Flag.LOOK));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (ellie.isSleeping()) return false;
|
||||
if (ellie.getWakeUpCooldown() > 0) return false;
|
||||
if (!ellie.isNightTime()) return false;
|
||||
return ellie.level().getGameTime() >= this.nextSearchAllowedTime;
|
||||
if (!ellie.level().isNight() && !ellie.isTired()) return false;
|
||||
|
||||
bedPos = findBed();
|
||||
return bedPos != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
return ellie.isNightTime() && !this.noBedAvailable;
|
||||
private BlockPos findBed() {
|
||||
BlockPos stored = ellie.getBedPos();
|
||||
if (stored != null && isFreeBed(stored) && ellie.distanceToSqr(Vec3.atCenterOf(stored)) < 50 * 50) {
|
||||
return stored;
|
||||
}
|
||||
if (scanCooldown-- > 0) return null;
|
||||
scanCooldown = SCAN_INTERVAL;
|
||||
return findNearestBed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
this.arrived = false;
|
||||
this.noBedAvailable = false;
|
||||
this.pathAttempts = 0;
|
||||
|
||||
if (!isBedValid(ellie.getBedPos())) {
|
||||
ellie.setBedPos(findNearbyBedFootPos());
|
||||
}
|
||||
|
||||
if (ellie.getBedPos() != null) {
|
||||
moveTowardsBed();
|
||||
} else {
|
||||
giveUpForNow();
|
||||
claimed = false;
|
||||
if (bedPos != null) {
|
||||
ellie.getNavigation().moveTo(bedPos.getX() + 0.5, bedPos.getY(), bedPos.getZ() + 0.5, 0.6);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
if (this.arrived) {
|
||||
if (bedPos == null) return;
|
||||
|
||||
tryOpenDoor();
|
||||
|
||||
double distSqr = ellie.distanceToSqr(Vec3.atCenterOf(bedPos));
|
||||
|
||||
if (distSqr < REACH_DIST_SQR) {
|
||||
ellie.getNavigation().stop();
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.noBedAvailable) {
|
||||
return;
|
||||
}
|
||||
if (!claimed) {
|
||||
claimed = ellie.occupyBed(bedPos);
|
||||
}
|
||||
|
||||
BlockPos bed = ellie.getBedPos();
|
||||
if (!isBedValid(bed)) {
|
||||
BlockPos replacement = findNearbyBedFootPos();
|
||||
ellie.setBedPos(replacement);
|
||||
if (replacement == null) {
|
||||
giveUpForNow();
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tryOpenDoor() {
|
||||
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;
|
||||
}
|
||||
moveTowardsBed();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ellie.blockPosition().distSqr(bed) <= REACH_DIST_SQR) {
|
||||
claimBed(bed);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ellie.getNavigation().isDone()) {
|
||||
this.pathAttempts++;
|
||||
if (this.pathAttempts > MAX_PATHFIND_ATTEMPTS) {
|
||||
ellie.setBedPos(null);
|
||||
giveUpForNow();
|
||||
return;
|
||||
}
|
||||
moveTowardsBed();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,70 +101,54 @@ public class EllieSleepGoal extends Goal {
|
||||
if (ellie.isSleeping()) {
|
||||
ellie.wakeUp();
|
||||
}
|
||||
ellie.getNavigation().stop();
|
||||
this.arrived = false;
|
||||
this.noBedAvailable = false;
|
||||
sleepTimer = 0;
|
||||
bedPos = null;
|
||||
claimed = false;
|
||||
}
|
||||
|
||||
private void giveUpForNow() {
|
||||
this.noBedAvailable = true;
|
||||
this.nextSearchAllowedTime = ellie.level().getGameTime() + NO_BED_RETRY_COOLDOWN_TICKS;
|
||||
}
|
||||
|
||||
private void moveTowardsBed() {
|
||||
BlockPos bed = ellie.getBedPos();
|
||||
ellie.getNavigation().moveTo(bed.getX() + 0.5, bed.getY(), bed.getZ() + 0.5, 1.0);
|
||||
}
|
||||
|
||||
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;
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
if (ellie.isSleeping()) {
|
||||
if (sleepTimer >= MAX_SLEEP_TICKS) return false;
|
||||
if (bedPos != null) {
|
||||
BlockState state = ellie.level().getBlockState(bedPos);
|
||||
if (!(state.getBlock() instanceof BedBlock)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (bedPos == null) return false;
|
||||
if (ellie.hurtTime > 10) return false;
|
||||
if (!ellie.level().isNight() && !ellie.isTired()) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
BedPart part = state.getValue(BedBlock.PART);
|
||||
BlockPos footPos = (part == BedPart.FOOT)
|
||||
? pos
|
||||
: pos.relative(state.getValue(BedBlock.FACING).getOpposite());
|
||||
private BlockPos findNearestBed() {
|
||||
BlockPos entityPos = ellie.blockPosition();
|
||||
BlockPos.MutableBlockPos mutable = new BlockPos.MutableBlockPos();
|
||||
BlockPos nearest = null;
|
||||
double nearestDist = Double.MAX_VALUE;
|
||||
|
||||
double distSq = ownPos.distSqr(footPos);
|
||||
if (distSq < bestDistSq) {
|
||||
bestDistSq = distSq;
|
||||
best = footPos.immutable();
|
||||
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 best;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package me.sashegdev.fabled_hearts.ai;
|
||||
|
||||
import me.sashegdev.fabled_hearts.entity.ellie.EllieEntity;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.entity.ai.goal.Goal;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@@ -15,11 +13,6 @@ public class FollowPlayerGoal extends Goal {
|
||||
private int cooldown;
|
||||
private static final double FOLLOW_RANGE = 16.0;
|
||||
private static final double FOLLOW_RANGE_SQR = FOLLOW_RANGE * FOLLOW_RANGE;
|
||||
private static final int RECALC_COOLDOWN = 5;
|
||||
private static final int STUCK_THRESHOLD = 40;
|
||||
private BlockPos lastStuckPos;
|
||||
private int stuckTicks;
|
||||
private int stuckCooldown;
|
||||
|
||||
public FollowPlayerGoal(EllieEntity ellie) {
|
||||
this.ellie = ellie;
|
||||
@@ -28,20 +21,8 @@ public class FollowPlayerGoal extends Goal {
|
||||
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (ellie.tickCount < 20) return false;
|
||||
if (ellie.isSleeping()) return false;
|
||||
if (ellie.hurtTime > 0) return false;
|
||||
|
||||
int followId = ellie.getFollowTargetId();
|
||||
if (followId > 0) {
|
||||
Entity e = ellie.level().getEntity(followId);
|
||||
if (e instanceof Player p && p.isAlive()) {
|
||||
target = p;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
float rel = ellie.getRelationshipPoints();
|
||||
if (rel < 50) return false;
|
||||
|
||||
@@ -54,9 +35,6 @@ public class FollowPlayerGoal extends Goal {
|
||||
@Override
|
||||
public void start() {
|
||||
cooldown = 0;
|
||||
stuckTicks = 0;
|
||||
stuckCooldown = 0;
|
||||
lastStuckPos = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -64,30 +42,26 @@ public class FollowPlayerGoal extends Goal {
|
||||
if (target == null) return;
|
||||
|
||||
double distSqr = ellie.distanceToSqr(target);
|
||||
float rel = ellie.getRelationshipPoints();
|
||||
|
||||
double minDist, maxDist;
|
||||
if (ellie.getFollowTargetId() == target.getId()) {
|
||||
minDist = 2.0; maxDist = 4.0;
|
||||
if (rel >= 100) {
|
||||
minDist = 1.5; maxDist = 3.0;
|
||||
} else if (rel >= 75) {
|
||||
minDist = 3.0; maxDist = 6.0;
|
||||
} else {
|
||||
float rel = ellie.getRelationshipPoints();
|
||||
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;
|
||||
}
|
||||
minDist = 6.0; maxDist = 10.0;
|
||||
}
|
||||
|
||||
if (distSqr > maxDist * maxDist) {
|
||||
if (--cooldown <= 0) {
|
||||
cooldown = RECALC_COOLDOWN;
|
||||
checkStuck();
|
||||
cooldown = 10;
|
||||
ellie.getNavigation().moveTo(target, 0.6);
|
||||
}
|
||||
} else if (distSqr < minDist * minDist) {
|
||||
ellie.getNavigation().stop();
|
||||
if (--cooldown <= 0) {
|
||||
cooldown = RECALC_COOLDOWN;
|
||||
cooldown = 10;
|
||||
Vec3 away = new Vec3(
|
||||
ellie.getX() - target.getX(),
|
||||
0,
|
||||
@@ -103,47 +77,15 @@ public class FollowPlayerGoal extends Goal {
|
||||
} else {
|
||||
ellie.getNavigation().stop();
|
||||
cooldown = 0;
|
||||
stuckTicks = 0;
|
||||
lastStuckPos = null;
|
||||
ellie.getLookControl().setLookAt(target, 30, 30);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkStuck() {
|
||||
if (stuckCooldown > 0) {
|
||||
stuckCooldown--;
|
||||
return;
|
||||
}
|
||||
|
||||
boolean found = ellie.getNavigation().moveTo(target, 0.6);
|
||||
if (!found) {
|
||||
stuckCooldown = 10;
|
||||
}
|
||||
|
||||
if (ellie.getNavigation().isInProgress()) {
|
||||
BlockPos currentPos = ellie.blockPosition();
|
||||
if (currentPos.equals(lastStuckPos)) {
|
||||
stuckTicks++;
|
||||
if (stuckTicks > STUCK_THRESHOLD) {
|
||||
ellie.getNavigation().stop();
|
||||
stuckTicks = 0;
|
||||
stuckCooldown = 20;
|
||||
}
|
||||
} else {
|
||||
stuckTicks = 0;
|
||||
}
|
||||
lastStuckPos = currentPos;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
if (target == null || !target.isAlive()) return false;
|
||||
if (ellie.isSleeping()) return false;
|
||||
if (ellie.hurtTime > 0) return false;
|
||||
if (ellie.getFollowTargetId() == target.getId()) {
|
||||
return ellie.isFollowingPlayer();
|
||||
}
|
||||
double distSqr = ellie.distanceToSqr(target);
|
||||
return distSqr <= FOLLOW_RANGE_SQR;
|
||||
}
|
||||
@@ -152,8 +94,5 @@ public class FollowPlayerGoal extends Goal {
|
||||
public void stop() {
|
||||
target = null;
|
||||
ellie.getNavigation().stop();
|
||||
stuckTicks = 0;
|
||||
stuckCooldown = 0;
|
||||
lastStuckPos = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,32 +14,22 @@ import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.network.syncher.EntityDataAccessor;
|
||||
import net.minecraft.network.syncher.EntityDataSerializers;
|
||||
import net.minecraft.network.syncher.SynchedEntityData;
|
||||
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;
|
||||
import net.minecraft.world.item.Items;
|
||||
import net.minecraft.world.entity.ai.attributes.AttributeSupplier;
|
||||
import net.minecraft.world.entity.ai.attributes.Attributes;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
|
||||
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
||||
import net.minecraft.world.entity.animal.Animal;
|
||||
import net.minecraft.world.entity.player.Player;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.BedBlock;
|
||||
import net.minecraft.world.level.block.DoorBlock;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.level.block.state.properties.BedPart;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.pathfinder.Node;
|
||||
import net.minecraft.world.level.pathfinder.Path;
|
||||
import net.minecraftforge.network.PacketDistributor;
|
||||
import me.sashegdev.fabled_hearts.network.DebugSyncPacket;
|
||||
import me.sashegdev.fabled_hearts.network.ModNetworking;
|
||||
import software.bernie.geckolib.animatable.GeoEntity;
|
||||
import software.bernie.geckolib.core.animatable.instance.AnimatableInstanceCache;
|
||||
import software.bernie.geckolib.core.animation.AnimatableManager;
|
||||
@@ -66,57 +56,23 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Boolean> DATA_MOVING =
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<Boolean> DATA_DEBUG =
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.BOOLEAN);
|
||||
private static final EntityDataAccessor<String> DATA_DEBUG_BRAIN =
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.STRING);
|
||||
private static final EntityDataAccessor<Integer> DATA_TURNING_TIMER =
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> DATA_DEBUG_ANIM =
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.INT);
|
||||
private static final EntityDataAccessor<Integer> DATA_FOLLOW_TARGET =
|
||||
SynchedEntityData.defineId(EllieEntity.class, EntityDataSerializers.INT);
|
||||
|
||||
private static final List<String> DEBUG_ANIMS = List.of(
|
||||
"idle1", "idle2", "idle3", "turn", "eat1", "fall",
|
||||
"sleep", "walkingsleepy", "idlesleepy",
|
||||
"walkingrain", "idleconrain",
|
||||
"shiftwalking", "shiftidle",
|
||||
"walkingsimple", "idleins"
|
||||
);
|
||||
|
||||
private final AnimatableInstanceCache cache = GeckoLibUtil.createInstanceCache(this);
|
||||
|
||||
private int ticksWithoutSleep;
|
||||
private float relationshipPoints;
|
||||
private float prevYRot;
|
||||
private float yawDeltaAccumulator;
|
||||
private BlockPos bedPos;
|
||||
private Direction lastBedDir;
|
||||
private double lastX, lastZ;
|
||||
private int moveDetectCooldown;
|
||||
private BlockPos lastOpenedDoor;
|
||||
private int doorCloseTimer;
|
||||
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;
|
||||
private boolean debugMode;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -142,72 +98,74 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
this.entityData.define(DATA_EATING, false);
|
||||
this.entityData.define(DATA_FALL_TICKS, 0);
|
||||
this.entityData.define(DATA_MOVING, false);
|
||||
this.entityData.define(DATA_DEBUG, false);
|
||||
this.entityData.define(DATA_DEBUG_BRAIN, "");
|
||||
this.entityData.define(DATA_TURNING_TIMER, 0);
|
||||
this.entityData.define(DATA_DEBUG_ANIM, 0);
|
||||
this.entityData.define(DATA_FOLLOW_TARGET, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerGoals() {
|
||||
BasicAI.addBasicGoals(this, this.goalSelector, 4);
|
||||
this.goalSelector.addGoal(1, new EllieSleepGoal(this));
|
||||
this.goalSelector.addGoal(1, new EllieSleepGoal(this, 30));
|
||||
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
|
||||
public void aiStep() {
|
||||
if (!level().isClientSide && !entityData.get(DATA_SLEEPING)) {
|
||||
boolean aheadLow = checkLowCeiling();
|
||||
if (aheadLow) {
|
||||
crouchTimer = Math.max(crouchTimer, 20);
|
||||
if (onGround()) {
|
||||
setPose(Pose.CROUCHING);
|
||||
}
|
||||
}
|
||||
}
|
||||
super.aiStep();
|
||||
tickMovementDetection();
|
||||
tickDoors();
|
||||
if (!level().isClientSide) {
|
||||
tickServer();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickMovementDetection() {
|
||||
if (moveDetectCooldown-- <= 0) {
|
||||
moveDetectCooldown = 2;
|
||||
double dx = getX() - lastX;
|
||||
double dz = getZ() - lastZ;
|
||||
lastX = getX();
|
||||
lastZ = getZ();
|
||||
}
|
||||
}
|
||||
|
||||
private void tickDoors() {
|
||||
if (lastOpenedDoor != null) {
|
||||
BlockState state = level().getBlockState(lastOpenedDoor);
|
||||
if (state.getBlock() instanceof DoorBlock && state.getValue(DoorBlock.OPEN)) {
|
||||
double dist = distanceToSqr(lastOpenedDoor.getX() + 0.5, lastOpenedDoor.getY(), lastOpenedDoor.getZ() + 0.5);
|
||||
if (dist > 0.5 * 0.5) {
|
||||
doorCloseTimer++;
|
||||
if (doorCloseTimer > 10) {
|
||||
level().setBlock(lastOpenedDoor, state.setValue(DoorBlock.OPEN, false), 3);
|
||||
lastOpenedDoor = null;
|
||||
doorCloseTimer = 0;
|
||||
}
|
||||
} else {
|
||||
doorCloseTimer = 0;
|
||||
}
|
||||
} else {
|
||||
lastOpenedDoor = null;
|
||||
doorCloseTimer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isMoving() {
|
||||
double dx = getX() - lastX;
|
||||
double dz = getZ() - lastZ;
|
||||
boolean posChanged = dx * dx + dz * dz > 1.0e-8;
|
||||
boolean navBusy = navigation.isInProgress() && !navigation.isDone();
|
||||
boolean hasVelocity = getDeltaMovement().horizontalDistanceSqr() > 0.001;
|
||||
return navBusy || hasVelocity;
|
||||
return posChanged || navBusy || hasVelocity;
|
||||
}
|
||||
|
||||
private void tickServer() {
|
||||
if (needsSleepRestore && bedPos != null && entityData.get(DATA_SLEEPING)) {
|
||||
setBedSleepPos(bedPos);
|
||||
needsSleepRestore = false;
|
||||
}
|
||||
|
||||
if (entityData.get(DATA_SLEEPING) && bedPos != null) {
|
||||
BlockState bedState = level().getBlockState(bedPos);
|
||||
if (!(bedState.getBlock() instanceof BedBlock)) {
|
||||
wakeUp();
|
||||
}
|
||||
}
|
||||
|
||||
if (wakeUpCooldown > 0) wakeUpCooldown--;
|
||||
|
||||
tickNightlySleepTracking();
|
||||
tickHomeFollowsOwnerSpawn();
|
||||
tickHomeBounds();
|
||||
|
||||
boolean isNight = level().isNight();
|
||||
boolean raining = level().isRainingAt(blockPosition());
|
||||
boolean lowCeiling = checkLowCeiling();
|
||||
boolean tired = missedNights >= SLEEPY_MISSED_NIGHTS_THRESHOLD;
|
||||
boolean tired = ticksWithoutSleep > 48000;
|
||||
boolean sleeping = entityData.get(DATA_SLEEPING);
|
||||
boolean falling = !onGround() && getDeltaMovement().y < -0.1;
|
||||
boolean moving = isMoving();
|
||||
@@ -224,10 +182,7 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
}
|
||||
|
||||
boolean wasCrouching = getPose() == Pose.CROUCHING;
|
||||
|
||||
if (lowCeiling) {
|
||||
crouchTimer = Math.max(crouchTimer, 20);
|
||||
} else if (wasCrouching) {
|
||||
if (wasCrouching) {
|
||||
crouchTimer = Math.max(0, crouchTimer - 1);
|
||||
}
|
||||
|
||||
@@ -237,28 +192,10 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
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);
|
||||
crouchTimer = 20;
|
||||
}
|
||||
} else if (wasCrouching && crouchTimer > 0) {
|
||||
setPose(Pose.CROUCHING);
|
||||
@@ -268,20 +205,6 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
}
|
||||
}
|
||||
|
||||
if (entityData.get(DATA_DEBUG) && tickCount % 10 == 0) {
|
||||
updateDebugBrainString();
|
||||
if (!level().isClientSide) {
|
||||
List<BlockPos> wps = getPathWaypoints();
|
||||
for (Player player : level().players()) {
|
||||
if (player.distanceToSqr(this) < 64 * 64) {
|
||||
ModNetworking.CHANNEL.send(
|
||||
PacketDistributor.PLAYER.with(() -> (ServerPlayer) player),
|
||||
new DebugSyncPacket(this.getId(), wps, getDebugBrainString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sleeping) {
|
||||
if (isNight) {
|
||||
ticksWithoutSleep++;
|
||||
@@ -289,231 +212,44 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
} else {
|
||||
ticksWithoutSleep = Math.max(0, ticksWithoutSleep - 10);
|
||||
}
|
||||
|
||||
int turnTimer = entityData.get(DATA_TURNING_TIMER);
|
||||
if (turnTimer > 0) {
|
||||
entityData.set(DATA_TURNING_TIMER, turnTimer - 1);
|
||||
if (turnTimer == 1) yawDeltaAccumulator = 0;
|
||||
} else if (!sleeping && !falling) {
|
||||
float yawDelta = Math.abs(getYRot() - prevYRot);
|
||||
yawDeltaAccumulator += yawDelta;
|
||||
if (yawDeltaAccumulator > 30 && !moving) {
|
||||
entityData.set(DATA_TURNING_TIMER, 17);
|
||||
yawDeltaAccumulator = 0;
|
||||
}
|
||||
}
|
||||
prevYRot = getYRot();
|
||||
|
||||
if (tickCount > 20) {
|
||||
int debugAnim = entityData.get(DATA_DEBUG_ANIM);
|
||||
if (debugAnim > 0) {
|
||||
debugAnimTimer--;
|
||||
if (debugAnimTimer <= 0) {
|
||||
entityData.set(DATA_DEBUG_ANIM, 0);
|
||||
}
|
||||
}
|
||||
|
||||
int followTarget = entityData.get(DATA_FOLLOW_TARGET);
|
||||
if (followTarget > 0) {
|
||||
Entity target = level().getEntity(followTarget);
|
||||
if (!(target instanceof Player) || !target.isAlive() || target.distanceToSqr(this) > 64 * 64) {
|
||||
entityData.set(DATA_FOLLOW_TARGET, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateDebugBrainString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String goalName = goalSelector.getRunningGoals()
|
||||
.findFirst()
|
||||
.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("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("|");
|
||||
sb.append("Rel:").append(String.format("%.0f", relationshipPoints)).append("|");
|
||||
sb.append("CrouchT:").append(crouchTimer).append("|");
|
||||
sb.append("TurnT:").append(entityData.get(DATA_TURNING_TIMER)).append("|");
|
||||
if (navigation.isInProgress() && !navigation.isDone()) {
|
||||
sb.append("NavTo:").append(navigation.getTargetPos());
|
||||
} else {
|
||||
sb.append("NavTo:-");
|
||||
}
|
||||
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)) {
|
||||
entityData.set(DATA_DEBUG_BRAIN, "");
|
||||
}
|
||||
}
|
||||
|
||||
private List<BlockPos> getPathWaypoints() {
|
||||
List<BlockPos> result = new ArrayList<>();
|
||||
Path path = navigation.getPath();
|
||||
if (path != null) {
|
||||
for (int i = 0; i < path.getNodeCount(); i++) {
|
||||
Node node = path.getNode(i);
|
||||
result.add(new BlockPos(node.x, node.y, node.z));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean checkLowCeiling() {
|
||||
BlockPos pos = blockPosition();
|
||||
if (isSpaceLow(pos)) return true;
|
||||
if (isMoving() && navigation.isInProgress()) {
|
||||
Direction dir = getDirection();
|
||||
if (isSpaceLow(pos.relative(dir, 1))) return true;
|
||||
}
|
||||
return false;
|
||||
return isSpaceLow(blockPosition());
|
||||
}
|
||||
|
||||
public boolean isSpaceLow(BlockPos pos) {
|
||||
for (int dy = 1; dy < 3; dy++) {
|
||||
if (level().getBlockState(pos.above(dy)).blocksMotion()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return level().getBlockState(pos.above(1)).blocksMotion();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EntityDimensions getDimensions(Pose pose) {
|
||||
if (pose == Pose.SLEEPING) return EntityDimensions.fixed(0.2f, 0.2f);
|
||||
if (pose == Pose.SLEEPING) return EntityDimensions.fixed(0.4f, 0.3f);
|
||||
if (pose == Pose.CROUCHING) return EntityDimensions.fixed(0.6f, 1.0f);
|
||||
return super.getDimensions(pose);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getEyeHeight(Pose pose) {
|
||||
if (pose == Pose.STANDING && getPose() == Pose.SLEEPING) return 0.1f;
|
||||
return super.getEyeHeight(pose);
|
||||
}
|
||||
|
||||
public boolean occupyBed(BlockPos pos) {
|
||||
BlockState state = level().getBlockState(pos);
|
||||
if (state.getBlock() instanceof BedBlock) {
|
||||
if (state.getValue(BedBlock.OCCUPIED)) return false;
|
||||
Direction facing = state.getValue(BedBlock.FACING);
|
||||
BlockPos foot = state.getValue(BedBlock.PART) == BedPart.HEAD
|
||||
? pos.relative(facing.getOpposite())
|
||||
: pos;
|
||||
BlockState footState = level().getBlockState(foot);
|
||||
if (footState.getBlock() instanceof BedBlock) {
|
||||
level().setBlock(foot, footState.setValue(BedBlock.OCCUPIED, true), 3);
|
||||
}
|
||||
BlockPos head = foot.relative(facing);
|
||||
BlockState headState = level().getBlockState(head);
|
||||
if (headState.getBlock() instanceof BedBlock) {
|
||||
level().setBlock(head, headState.setValue(BedBlock.OCCUPIED, true), 3);
|
||||
}
|
||||
bedPos = foot;
|
||||
return true;
|
||||
if (!(state.getBlock() instanceof BedBlock)) return false;
|
||||
if (state.getValue(BedBlock.OCCUPIED)) return false;
|
||||
|
||||
Direction facing = state.getValue(BedBlock.FACING);
|
||||
BlockPos foot = state.getValue(BedBlock.PART) == BedPart.HEAD
|
||||
? pos.relative(facing.getOpposite())
|
||||
: pos;
|
||||
BlockPos head = foot.relative(facing);
|
||||
|
||||
BlockState footState = level().getBlockState(foot);
|
||||
if (footState.getBlock() instanceof BedBlock) {
|
||||
level().setBlock(foot, footState.setValue(BedBlock.OCCUPIED, true), 3);
|
||||
}
|
||||
return false;
|
||||
BlockState headState = level().getBlockState(head);
|
||||
if (headState.getBlock() instanceof BedBlock) {
|
||||
level().setBlock(head, headState.setValue(BedBlock.OCCUPIED, true), 3);
|
||||
}
|
||||
bedPos = foot;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void releaseBed() {
|
||||
@@ -539,40 +275,19 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
return level().getBlockState(pos).isBed(level(), pos, null);
|
||||
}
|
||||
|
||||
public boolean isBedAtOrAdjacent(BlockPos pos) {
|
||||
if (pos.equals(bedPos)) return true;
|
||||
if (bedPos != null) {
|
||||
BlockState state = level().getBlockState(bedPos);
|
||||
if (state.getBlock() instanceof BedBlock) {
|
||||
BlockPos head = bedPos.relative(state.getValue(BedBlock.FACING));
|
||||
if (pos.equals(head)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setBedSleepPos(BlockPos pos) {
|
||||
BlockState state = level().getBlockState(pos);
|
||||
if (!(state.getBlock() instanceof BedBlock)) return;
|
||||
|
||||
Direction facing = state.getValue(BedBlock.FACING);
|
||||
BlockPos footPos = state.getValue(BedBlock.PART) == BedPart.HEAD
|
||||
? pos.relative(facing.getOpposite())
|
||||
: pos;
|
||||
BlockPos headPos = state.getValue(BedBlock.PART) == BedPart.HEAD
|
||||
? pos
|
||||
: pos.relative(facing);
|
||||
|
||||
setPose(Pose.SLEEPING);
|
||||
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;
|
||||
setPos(headPos.getX() + 0.5, pos.getY() + 0.6875, headPos.getZ() + 0.5);
|
||||
lastBedDir = facing;
|
||||
setSleeping(true);
|
||||
setPose(Pose.SLEEPING);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -589,47 +304,26 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
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;
|
||||
setDeltaMovement(getDeltaMovement().add(0, 0.15, 0));
|
||||
}
|
||||
|
||||
public void openDoorInFront() {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
BlockPos checkPos = blockPosition().relative(getDirection(), 2 + i);
|
||||
BlockState state = level().getBlockState(checkPos);
|
||||
if (state.getBlock() instanceof DoorBlock) {
|
||||
if (!state.getValue(DoorBlock.OPEN)) {
|
||||
level().setBlock(checkPos, state.setValue(DoorBlock.OPEN, true), 3);
|
||||
lastOpenedDoor = checkPos;
|
||||
doorCloseTimer = 0;
|
||||
}
|
||||
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
|
||||
@@ -638,11 +332,6 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
}
|
||||
|
||||
private <E extends GeoEntity> PlayState animationHandler(AnimationState<E> state) {
|
||||
int debugAnim = entityData.get(DATA_DEBUG_ANIM);
|
||||
if (debugAnim > 0 && debugAnim <= DEBUG_ANIMS.size()) {
|
||||
return state.setAndContinue(RawAnimation.begin().thenPlay(DEBUG_ANIMS.get(debugAnim - 1)));
|
||||
}
|
||||
|
||||
int idleAnim = entityData.get(DATA_IDLE_ANIM);
|
||||
if (idleAnim > 0) {
|
||||
String animName = switch (idleAnim) {
|
||||
@@ -683,10 +372,6 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
if (fallTicks > 5) return RawAnimation.begin().thenPlay("fall");
|
||||
if (eating) return RawAnimation.begin().thenPlay("eat1");
|
||||
|
||||
if (entityData.get(DATA_TURNING_TIMER) > 0) {
|
||||
return RawAnimation.begin().thenPlay("turn").thenLoop(resolveTurnLoopName());
|
||||
}
|
||||
|
||||
if (tired) return moving
|
||||
? RawAnimation.begin().thenLoop("walkingsleepy")
|
||||
: RawAnimation.begin().thenLoop("idlesleepy");
|
||||
@@ -700,19 +385,6 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
return RawAnimation.begin().thenLoop("idleins");
|
||||
}
|
||||
|
||||
private String resolveTurnLoopName() {
|
||||
boolean tired = entityData.get(DATA_TIRED);
|
||||
boolean inRain = entityData.get(DATA_IN_RAIN);
|
||||
boolean lowCeiling = entityData.get(DATA_UNDER_LOW_CEILING);
|
||||
boolean moving = entityData.get(DATA_MOVING);
|
||||
|
||||
if (tired) return moving ? "walkingsleepy" : "idlesleepy";
|
||||
if (inRain) return moving ? "walkingrain" : "idleconrain";
|
||||
if (lowCeiling) return moving ? "shiftwalking" : "shiftidle";
|
||||
if (moving) return "walkingsimple";
|
||||
return "idleins";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AgeableMob getBreedOffspring(ServerLevel level, AgeableMob other) {
|
||||
return null;
|
||||
@@ -720,31 +392,6 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
|
||||
@Override
|
||||
public InteractionResult mobInteract(Player player, InteractionHand hand) {
|
||||
ItemStack stack = player.getItemInHand(hand);
|
||||
boolean holdingStick = stack.is(Items.DEBUG_STICK);
|
||||
|
||||
if (holdingStick && player.isShiftKeyDown()) {
|
||||
if (!level().isClientSide) {
|
||||
int current = entityData.get(DATA_FOLLOW_TARGET);
|
||||
if (current == player.getId()) {
|
||||
entityData.set(DATA_FOLLOW_TARGET, 0);
|
||||
} else {
|
||||
entityData.set(DATA_FOLLOW_TARGET, player.getId());
|
||||
}
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
if (holdingStick) {
|
||||
if (!level().isClientSide) {
|
||||
int current = entityData.get(DATA_DEBUG_ANIM);
|
||||
int next = (current + 1) % (DEBUG_ANIMS.size() + 1);
|
||||
entityData.set(DATA_DEBUG_ANIM, next);
|
||||
debugAnimTimer = 200;
|
||||
}
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
if (player.isShiftKeyDown()) return InteractionResult.PASS;
|
||||
|
||||
if (entityData.get(DATA_SLEEPING)) {
|
||||
@@ -752,6 +399,9 @@ 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;
|
||||
@@ -759,43 +409,12 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
return InteractionResult.SUCCESS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hurt(DamageSource source, float amount) {
|
||||
if (source.getEntity() instanceof Player player) {
|
||||
ItemStack weapon = player.getMainHandItem();
|
||||
if (weapon.is(Items.DEBUG_STICK)) {
|
||||
if (!level().isClientSide) {
|
||||
int current = entityData.get(DATA_DEBUG_ANIM);
|
||||
if (current > 0) {
|
||||
entityData.set(DATA_DEBUG_ANIM, current);
|
||||
debugAnimTimer = 200;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (isSleeping() && !level().isClientSide) {
|
||||
wakeUp();
|
||||
}
|
||||
return super.hurt(source, amount);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAdditionalSaveData(CompoundTag tag) {
|
||||
super.addAdditionalSaveData(tag);
|
||||
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
|
||||
@@ -803,22 +422,7 @@ public class EllieEntity extends Animal implements GeoEntity {
|
||||
super.readAdditionalSaveData(tag);
|
||||
ticksWithoutSleep = tag.getInt("TicksWithoutSleep");
|
||||
relationshipPoints = tag.getFloat("Relationship");
|
||||
if (tag.contains("BedPos")) {
|
||||
bedPos = BlockPos.of(tag.getLong("BedPos"));
|
||||
if (entityData.get(DATA_SLEEPING)) {
|
||||
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");
|
||||
}
|
||||
if (tag.contains("BedPos")) bedPos = BlockPos.of(tag.getLong("BedPos"));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -829,8 +433,6 @@ 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 boolean isDebugMode() { return entityData.get(DATA_DEBUG); }
|
||||
public String getDebugBrainString() { return entityData.get(DATA_DEBUG_BRAIN); }
|
||||
public void setBedPos(BlockPos pos) { this.bedPos = pos; }
|
||||
public BlockPos getBedPos() { return bedPos; }
|
||||
public float getRelationshipPoints() { return relationshipPoints; }
|
||||
@@ -838,8 +440,6 @@ 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); }
|
||||
public boolean isDebugMode() { return debugMode; }
|
||||
public void toggleDebug() { this.debugMode = !debugMode; }
|
||||
}
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
package me.sashegdev.fabled_hearts.entity.ellie;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.mojang.blaze3d.vertex.VertexConsumer;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Font;
|
||||
import net.minecraft.client.renderer.MultiBufferSource;
|
||||
import com.mojang.math.Axis;
|
||||
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;
|
||||
import org.joml.Matrix4f;
|
||||
import software.bernie.geckolib.renderer.GeoEntityRenderer;
|
||||
|
||||
public class EllieRenderer extends GeoEntityRenderer<EllieEntity> {
|
||||
@@ -21,60 +18,33 @@ public class EllieRenderer extends GeoEntityRenderer<EllieEntity> {
|
||||
|
||||
@Override
|
||||
public RenderType getRenderType(EllieEntity animatable, ResourceLocation texture,
|
||||
@Nullable MultiBufferSource bufferSource, float partialTick) {
|
||||
@Nullable net.minecraft.client.renderer.MultiBufferSource bufferSource, float partialTick) {
|
||||
return RenderType.entityTranslucent(texture);
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
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) bedDir = entity.getLastBedDir();
|
||||
if (bedDir != null) {
|
||||
float targetYRot = bedDir.toYRot();
|
||||
// North/South fix: model lies on side without this
|
||||
if (bedDir == Direction.NORTH || bedDir == Direction.SOUTH) {
|
||||
targetYRot += 180.0F;
|
||||
}
|
||||
float offset = 1.655F;
|
||||
poseStack.translate(
|
||||
-bedDir.getStepX() * offset,
|
||||
0.125F,
|
||||
-bedDir.getStepZ() * offset
|
||||
);
|
||||
poseStack.mulPose(Axis.YP.rotationDegrees(targetYRot));
|
||||
poseStack.mulPose(Axis.XP.rotationDegrees(90.0F));
|
||||
poseStack.mulPose(Axis.ZP.rotationDegrees(180.0F));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void renderBrainText(EllieEntity entity, PoseStack poseStack,
|
||||
MultiBufferSource bufferSource, int packedLight) {
|
||||
String brain = entity.getDebugBrainString();
|
||||
if (brain == null || brain.isEmpty()) return;
|
||||
|
||||
Font font = Minecraft.getInstance().font;
|
||||
String[] lines = brain.split("\\|");
|
||||
|
||||
poseStack.pushPose();
|
||||
float heightOffset = entity.getPose() == Pose.SLEEPING ? 1.8f : entity.getBbHeight() + 0.7f;
|
||||
poseStack.translate(0, heightOffset, 0);
|
||||
poseStack.mulPose(Minecraft.getInstance().getEntityRenderDispatcher().cameraOrientation());
|
||||
poseStack.scale(-0.025f, -0.025f, 0.025f);
|
||||
|
||||
int totalHeight = lines.length * (font.lineHeight + 2);
|
||||
int startY = -totalHeight / 2;
|
||||
|
||||
for (String line : lines) {
|
||||
int color = 0xFFFFFF;
|
||||
if (line.startsWith("Goal:")) color = 0x55FF55;
|
||||
else if (line.startsWith("Bed:")) color = 0xAAAAAA;
|
||||
else if (line.startsWith("Sleep:")) color = 0xFFFF55;
|
||||
else if (line.startsWith("Tired:")) color = 0xFFAA00;
|
||||
else if (line.startsWith("Move:")) color = 0x55FFFF;
|
||||
else if (line.startsWith("Pose:")) color = 0xFF55FF;
|
||||
else if (line.startsWith("LowCeil:")) color = 0xFF5555;
|
||||
else if (line.startsWith("Rel:")) color = 0xFF69B4;
|
||||
else if (line.startsWith("CrouchT:")) color = 0xAAAAAA;
|
||||
else if (line.startsWith("NavTo:")) color = 0x55FFAA;
|
||||
else if (line.startsWith("TurnT:")) color = 0xFFAAFF;
|
||||
|
||||
int width = font.width(line);
|
||||
int x = -width / 2;
|
||||
|
||||
Matrix4f matrix = poseStack.last().pose();
|
||||
font.drawInBatch(line, x, startY, color, false,
|
||||
matrix, bufferSource, Font.DisplayMode.SEE_THROUGH,
|
||||
0x40000000, packedLight);
|
||||
startY += font.lineHeight + 2;
|
||||
}
|
||||
|
||||
poseStack.popPose();
|
||||
super.applyRotations(entity, poseStack, tick, yRot, partialTick);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,9 +40,6 @@ 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 появилась!"));
|
||||
|
||||
@@ -15,7 +15,7 @@ public class ModEntities {
|
||||
|
||||
public static final RegistryObject<EntityType<EllieEntity>> ELLIE =
|
||||
ENTITIES.register("ellie", () -> EntityType.Builder.of(EllieEntity::new, MobCategory.CREATURE)
|
||||
.sized(0.6f, 2.5f)
|
||||
.sized(0.9f, 2.5f)
|
||||
.clientTrackingRange(64)
|
||||
.build("ellie"));
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user