Compare commits

7 Commits

10 changed files with 4919 additions and 21984 deletions
Vendored Executable → Regular
View File
@@ -0,0 +1,39 @@
--- 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
@@ -0,0 +1,59 @@
--- 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,6 +19,7 @@ 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;
@@ -66,13 +67,13 @@ public class Main {
}
@SubscribeEvent
public static void registerKeyMappings(RegisterKeyMappingsEvent event) {
DebugClientHandler.registerKeyMapping(event);
public static void onClientSetup(FMLClientSetupEvent event) {
DebugClientHandler.init();
}
@SubscribeEvent
public static void onClientSetup(net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent event) {
DebugClientHandler.init();
public static void registerKeyMappings(RegisterKeyMappingsEvent event) {
DebugClientHandler.registerKeyMapping(event);
}
}
@@ -88,10 +89,10 @@ public class Main {
var pos = event.getPos();
event.getLevel().getEntitiesOfClass(
EllieEntity.class,
new net.minecraft.world.phys.AABB(pos).inflate(2),
e -> e.isSleeping() && pos.equals(e.getBedPos())
new net.minecraft.world.phys.AABB(pos).inflate(3),
e -> e.isBedAtOrAdjacent(pos) || (e.getBedPos() != null && e.getBedPos().equals(pos))
).forEach(e -> {
e.wakeUp();
if (e.isSleeping()) e.wakeUp();
e.forgetBed();
});
}
@@ -4,7 +4,6 @@ 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.phys.Vec3;
@@ -16,9 +15,7 @@ public class EllieSleepGoal extends Goal {
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, int searchRadius) {
@@ -41,8 +38,6 @@ public class EllieSleepGoal extends Goal {
if (stored != null && isFreeBed(stored) && ellie.distanceToSqr(Vec3.atCenterOf(stored)) < 50 * 50) {
return stored;
}
if (scanCooldown-- > 0) return null;
scanCooldown = SCAN_INTERVAL;
return findNearestBed();
}
@@ -58,7 +53,11 @@ public class EllieSleepGoal extends Goal {
public void tick() {
if (bedPos == null) return;
tryOpenDoor();
BlockState currentState = ellie.level().getBlockState(bedPos);
if (!(currentState.getBlock() instanceof BedBlock)) {
bedPos = null;
return;
}
double distSqr = ellie.distanceToSqr(Vec3.atCenterOf(bedPos));
@@ -83,19 +82,6 @@ public class EllieSleepGoal extends Goal {
}
}
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;
}
}
}
@Override
public void stop() {
if (ellie.isSleeping()) {
@@ -108,16 +94,21 @@ public class EllieSleepGoal extends Goal {
@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 (bedPos != null) {
BlockState state = ellie.level().getBlockState(bedPos);
if (!(state.getBlock() instanceof BedBlock)) 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;
}
@@ -1,6 +1,8 @@
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;
@@ -13,6 +15,11 @@ 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;
@@ -21,8 +28,20 @@ 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;
@@ -35,6 +54,9 @@ public class FollowPlayerGoal extends Goal {
@Override
public void start() {
cooldown = 0;
stuckTicks = 0;
stuckCooldown = 0;
lastStuckPos = null;
}
@Override
@@ -42,26 +64,30 @@ public class FollowPlayerGoal extends Goal {
if (target == null) return;
double distSqr = ellie.distanceToSqr(target);
float rel = ellie.getRelationshipPoints();
double minDist, maxDist;
if (rel >= 100) {
minDist = 1.5; maxDist = 3.0;
} else if (rel >= 75) {
minDist = 3.0; maxDist = 6.0;
if (ellie.getFollowTargetId() == target.getId()) {
minDist = 2.0; maxDist = 4.0;
} else {
minDist = 6.0; maxDist = 10.0;
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;
}
}
if (distSqr > maxDist * maxDist) {
if (--cooldown <= 0) {
cooldown = 10;
ellie.getNavigation().moveTo(target, 0.6);
cooldown = RECALC_COOLDOWN;
checkStuck();
}
} else if (distSqr < minDist * minDist) {
ellie.getNavigation().stop();
if (--cooldown <= 0) {
cooldown = 10;
cooldown = RECALC_COOLDOWN;
Vec3 away = new Vec3(
ellie.getX() - target.getX(),
0,
@@ -77,15 +103,47 @@ 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;
}
@@ -94,5 +152,8 @@ public class FollowPlayerGoal extends Goal {
public void stop() {
target = null;
ellie.getNavigation().stop();
stuckTicks = 0;
stuckCooldown = 0;
lastStuckPos = null;
}
}
@@ -14,22 +14,31 @@ 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.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;
@@ -56,23 +65,44 @@ 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 boolean debugMode;
private int debugAnimTimer;
private boolean needsSleepRestore;
public EllieEntity(EntityType<? extends Animal> type, Level level) {
super(type, level);
((GroundPathNavigation) this.getNavigation()).setCanOpenDoors(true);
this.setPersistenceRequired();
lastX = getX();
lastZ = getZ();
prevYRot = getYRot();
}
@Override
@@ -98,6 +128,11 @@ 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
@@ -112,56 +147,42 @@ public class EllieEntity extends Animal implements GeoEntity {
@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 posChanged || navBusy || hasVelocity;
return 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();
}
}
boolean isNight = level().isNight();
boolean raining = level().isRainingAt(blockPosition());
boolean lowCeiling = checkLowCeiling();
@@ -182,20 +203,19 @@ public class EllieEntity extends Animal implements GeoEntity {
}
boolean wasCrouching = getPose() == Pose.CROUCHING;
if (wasCrouching) {
if (lowCeiling) {
crouchTimer = Math.max(crouchTimer, 20);
} else if (wasCrouching) {
crouchTimer = Math.max(0, crouchTimer - 1);
}
if (sleeping) {
setPose(Pose.SLEEPING);
crouchTimer = 0;
if (bedPos != null && distanceToSqr(Vec3.atCenterOf(bedPos)) > 9.0) {
wakeUp();
}
} else if (lowCeiling) {
if (onGround() || wasCrouching) {
setPose(Pose.CROUCHING);
crouchTimer = 20;
}
} else if (wasCrouching && crouchTimer > 0) {
setPose(Pose.CROUCHING);
@@ -205,6 +225,20 @@ 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));
}
}
}
}
if (!sleeping) {
if (isNight) {
ticksWithoutSleep++;
@@ -212,44 +246,136 @@ 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("Bed:").append(bedPos != null ? bedPos : "-").append("|");
sb.append("Sleep:").append(entityData.get(DATA_SLEEPING)).append("|");
sb.append("Tired:").append(ticksWithoutSleep).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 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() {
return isSpaceLow(blockPosition());
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;
}
public boolean isSpaceLow(BlockPos pos) {
return level().getBlockState(pos.above(1)).blocksMotion();
for (int dy = 1; dy < 3; dy++) {
if (level().getBlockState(pos.above(dy)).blocksMotion()) {
return true;
}
}
return false;
}
@Override
public EntityDimensions getDimensions(Pose pose) {
if (pose == Pose.SLEEPING) return EntityDimensions.fixed(0.4f, 0.3f);
if (pose == Pose.SLEEPING) return EntityDimensions.fixed(0.15f, 0.15f);
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)) 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);
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;
}
BlockState headState = level().getBlockState(head);
if (headState.getBlock() instanceof BedBlock) {
level().setBlock(head, headState.setValue(BedBlock.OCCUPIED, true), 3);
}
bedPos = foot;
return true;
return false;
}
public void releaseBed() {
@@ -275,6 +401,18 @@ 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;
@@ -284,54 +422,36 @@ public class EllieEntity extends Animal implements GeoEntity {
? pos
: pos.relative(facing);
setPos(headPos.getX() + 0.5, pos.getY() + 0.6875, headPos.getZ() + 0.5);
lastBedDir = facing;
setSleeping(true);
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);
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() {
releaseBed();
forgetBed();
setSleeping(false);
setPose(Pose.STANDING);
navigation.stop();
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;
}
}
}
@Override
public void registerControllers(AnimatableManager.ControllerRegistrar controllers) {
controllers.add(new AnimationController<>(this, "main", 4, this::animationHandler));
}
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) {
@@ -372,6 +492,10 @@ 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");
@@ -385,6 +509,19 @@ 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;
@@ -392,6 +529,31 @@ 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)) {
@@ -409,6 +571,24 @@ 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;
}
}
return super.hurt(source, amount);
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
@@ -422,7 +602,12 @@ 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 (tag.contains("BedPos")) {
bedPos = BlockPos.of(tag.getLong("BedPos"));
if (entityData.get(DATA_SLEEPING)) {
needsSleepRestore = true;
}
}
}
@Override
@@ -433,6 +618,8 @@ 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; }
@@ -440,6 +627,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 boolean isDebugMode() { return debugMode; }
public void toggleDebug() { this.debugMode = !debugMode; }
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); }
}
@@ -1,13 +1,18 @@
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;
import org.joml.Matrix4f;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class EllieRenderer extends GeoEntityRenderer<EllieEntity> {
@@ -16,35 +21,82 @@ 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 net.minecraft.client.renderer.MultiBufferSource bufferSource, float partialTick) {
@Nullable MultiBufferSource bufferSource, float partialTick) {
return RenderType.entityTranslucent(texture);
}
@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) 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;
}
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);
}
super.applyRotations(entity, poseStack, tick, yRot, partialTick);
}
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();
}
}
@@ -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.9f, 2.5f)
.sized(0.6f, 2.5f)
.clientTrackingRange(64)
.build("ellie"));
File diff suppressed because it is too large Load Diff