194 lines
6.1 KiB
Java
194 lines
6.1 KiB
Java
package net.minecraft.world.entity.monster.creaking;
|
|
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Vec3i;
|
|
import net.minecraft.core.particles.BlockParticleOption;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.tags.DamageTypeTags;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
|
|
import net.minecraft.world.entity.ai.navigation.PathNavigation;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.Blocks;
|
|
import net.minecraft.world.level.block.CreakingHeartBlock;
|
|
import net.minecraft.world.level.block.entity.CreakingHeartBlockEntity;
|
|
import net.minecraft.world.level.pathfinder.PathFinder;
|
|
import net.minecraft.world.level.pathfinder.PathType;
|
|
import net.minecraft.world.level.pathfinder.PathfindingContext;
|
|
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class CreakingTransient extends Creaking {
|
|
public static final int INVULNERABILITY_ANIMATION_DURATION = 8;
|
|
private int invulnerabilityAnimationRemainingTicks;
|
|
@Nullable
|
|
BlockPos homePos;
|
|
|
|
public CreakingTransient(EntityType<? extends Creaking> entityType, Level level) {
|
|
super(entityType, level);
|
|
}
|
|
|
|
public void bindToCreakingHeart(BlockPos blockPos) {
|
|
this.homePos = blockPos;
|
|
}
|
|
|
|
@Override
|
|
public boolean hurtServer(ServerLevel serverLevel, DamageSource damageSource, float f) {
|
|
if (this.level().isClientSide) {
|
|
return super.hurtServer(serverLevel, damageSource, f);
|
|
} else if (damageSource.is(DamageTypeTags.BYPASSES_INVULNERABILITY)) {
|
|
return super.hurtServer(serverLevel, damageSource, f);
|
|
} else if (!this.isInvulnerableTo(serverLevel, damageSource) && this.invulnerabilityAnimationRemainingTicks <= 0) {
|
|
this.invulnerabilityAnimationRemainingTicks = 8;
|
|
this.level().broadcastEntityEvent(this, (byte)66);
|
|
if (this.level().getBlockEntity(this.homePos) instanceof CreakingHeartBlockEntity creakingHeartBlockEntity && creakingHeartBlockEntity.isProtector(this)) {
|
|
if (damageSource.getEntity() instanceof Player) {
|
|
creakingHeartBlockEntity.creakingHurt();
|
|
}
|
|
|
|
this.playHurtSound(damageSource);
|
|
}
|
|
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void aiStep() {
|
|
if (this.invulnerabilityAnimationRemainingTicks > 0) {
|
|
this.invulnerabilityAnimationRemainingTicks--;
|
|
}
|
|
|
|
super.aiStep();
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (this.level().isClientSide
|
|
|| this.homePos != null
|
|
&& this.level().getBlockEntity(this.homePos) instanceof CreakingHeartBlockEntity creakingHeartBlockEntity
|
|
&& creakingHeartBlockEntity.isProtector(this)) {
|
|
super.tick();
|
|
if (this.level().isClientSide) {
|
|
this.setupAnimationStates();
|
|
}
|
|
} else {
|
|
this.setRemoved(Entity.RemovalReason.DISCARDED);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void handleEntityEvent(byte id) {
|
|
if (id == 66) {
|
|
this.invulnerabilityAnimationRemainingTicks = 8;
|
|
this.playHurtSound(this.damageSources().generic());
|
|
} else {
|
|
super.handleEntityEvent(id);
|
|
}
|
|
}
|
|
|
|
private void setupAnimationStates() {
|
|
this.invulnerabilityAnimationState.animateWhen(this.invulnerabilityAnimationRemainingTicks > 0, this.tickCount);
|
|
}
|
|
|
|
public void tearDown(@Nullable DamageSource damageSource) {
|
|
if (this.level() instanceof ServerLevel serverLevel) {
|
|
AABB aABB = this.getBoundingBox();
|
|
Vec3 vec3 = aABB.getCenter();
|
|
double d = aABB.getXsize() * 0.3;
|
|
double e = aABB.getYsize() * 0.3;
|
|
double f = aABB.getZsize() * 0.3;
|
|
serverLevel.sendParticles(
|
|
new BlockParticleOption(ParticleTypes.BLOCK_CRUMBLE, Blocks.PALE_OAK_WOOD.defaultBlockState()), vec3.x, vec3.y, vec3.z, 100, d, e, f, 0.0
|
|
);
|
|
serverLevel.sendParticles(
|
|
new BlockParticleOption(
|
|
ParticleTypes.BLOCK_CRUMBLE, Blocks.CREAKING_HEART.defaultBlockState().setValue(CreakingHeartBlock.CREAKING, CreakingHeartBlock.CreakingHeartState.ACTIVE)
|
|
),
|
|
vec3.x,
|
|
vec3.y,
|
|
vec3.z,
|
|
10,
|
|
d,
|
|
e,
|
|
f,
|
|
0.0
|
|
);
|
|
}
|
|
|
|
this.makeSound(this.getDeathSound());
|
|
if (this.deathScore >= 0 && damageSource != null && damageSource.getEntity() instanceof LivingEntity livingEntity) {
|
|
livingEntity.awardKillScore(this, this.deathScore, damageSource);
|
|
}
|
|
|
|
this.remove(Entity.RemovalReason.DISCARDED);
|
|
}
|
|
|
|
@Override
|
|
protected boolean canAddPassenger(Entity passenger) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected boolean couldAcceptPassenger() {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected void addPassenger(Entity passenger) {
|
|
throw new IllegalStateException("Should never addPassenger without checking couldAcceptPassenger()");
|
|
}
|
|
|
|
@Override
|
|
public boolean canUsePortal(boolean allowPassengers) {
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
protected PathNavigation createNavigation(Level level) {
|
|
return new CreakingTransient.CreakingPathNavigation(this, level);
|
|
}
|
|
|
|
class CreakingPathNavigation extends GroundPathNavigation {
|
|
CreakingPathNavigation(final Creaking creaking, final Level level) {
|
|
super(creaking, level);
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (CreakingTransient.this.canMove()) {
|
|
super.tick();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected PathFinder createPathFinder(int maxVisitedNodes) {
|
|
this.nodeEvaluator = CreakingTransient.this.new HomeNodeEvaluator();
|
|
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
|
|
}
|
|
}
|
|
|
|
class HomeNodeEvaluator extends WalkNodeEvaluator {
|
|
private static final int MAX_DISTANCE_TO_HOME_SQ = 1024;
|
|
|
|
@Override
|
|
public PathType getPathType(PathfindingContext context, int x, int y, int z) {
|
|
BlockPos blockPos = CreakingTransient.this.homePos;
|
|
if (blockPos == null) {
|
|
return super.getPathType(context, x, y, z);
|
|
} else {
|
|
double d = blockPos.distSqr(new Vec3i(x, y, z));
|
|
return d > 1024.0 && d >= blockPos.distSqr(context.mobPosition()) ? PathType.BLOCKED : super.getPathType(context, x, y, z);
|
|
}
|
|
}
|
|
}
|
|
}
|