minecraft-src/net/minecraft/world/level/block/entity/CreakingHeartBlockEntity.java
2025-07-04 02:00:41 +03:00

219 lines
8.9 KiB
Java

package net.minecraft.world.level.block.entity;
import java.util.Optional;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.particles.TargetColorParticleOption;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.util.SpawnUtil;
import net.minecraft.world.Difficulty;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.EntitySpawnReason;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.monster.creaking.Creaking;
import net.minecraft.world.entity.monster.creaking.CreakingTransient;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.GameRules;
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.state.BlockState;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.phys.Vec3;
import org.jetbrains.annotations.Nullable;
public class CreakingHeartBlockEntity extends BlockEntity {
private static final int PLAYER_DETECTION_RANGE = 32;
public static final int CREAKING_ROAMING_RADIUS = 32;
private static final int DISTANCE_CREAKING_TOO_FAR = 34;
private static final int SPAWN_RANGE_XZ = 16;
private static final int SPAWN_RANGE_Y = 8;
private static final int ATTEMPTS_PER_SPAWN = 5;
private static final int UPDATE_TICKS = 20;
private static final int HURT_CALL_TOTAL_TICKS = 100;
private static final int NUMBER_OF_HURT_CALLS = 10;
private static final int HURT_CALL_INTERVAL = 10;
private static final int HURT_CALL_PARTICLE_TICKS = 50;
@Nullable
private CreakingTransient creaking;
private int ticker;
private int emitter;
@Nullable
private Vec3 emitterTarget;
private int outputSignal;
public CreakingHeartBlockEntity(BlockPos blockPos, BlockState blockState) {
super(BlockEntityType.CREAKING_HEART, blockPos, blockState);
}
public static void serverTick(Level level, BlockPos blockPos, BlockState blockState, CreakingHeartBlockEntity creakingHeartBlockEntity) {
int i = creakingHeartBlockEntity.computeAnalogOutputSignal();
if (creakingHeartBlockEntity.outputSignal != i) {
creakingHeartBlockEntity.outputSignal = i;
level.updateNeighbourForOutputSignal(blockPos, Blocks.CREAKING_HEART);
}
if (creakingHeartBlockEntity.emitter > 0) {
if (creakingHeartBlockEntity.emitter > 50) {
creakingHeartBlockEntity.emitParticles((ServerLevel)level, 1, true);
creakingHeartBlockEntity.emitParticles((ServerLevel)level, 1, false);
}
if (creakingHeartBlockEntity.emitter % 10 == 0 && level instanceof ServerLevel serverLevel && creakingHeartBlockEntity.emitterTarget != null) {
if (creakingHeartBlockEntity.creaking != null) {
creakingHeartBlockEntity.emitterTarget = creakingHeartBlockEntity.creaking.getBoundingBox().getCenter();
}
Vec3 vec3 = Vec3.atCenterOf(blockPos);
float f = 0.2F + 0.8F * (100 - creakingHeartBlockEntity.emitter) / 100.0F;
Vec3 vec32 = vec3.subtract(creakingHeartBlockEntity.emitterTarget).scale(f).add(creakingHeartBlockEntity.emitterTarget);
BlockPos blockPos2 = BlockPos.containing(vec32);
float g = creakingHeartBlockEntity.emitter / 2.0F / 100.0F + 0.5F;
serverLevel.playSound(null, blockPos2, SoundEvents.CREAKING_HEART_HURT, SoundSource.BLOCKS, g, 1.0F);
}
creakingHeartBlockEntity.emitter--;
}
if (creakingHeartBlockEntity.ticker-- < 0) {
creakingHeartBlockEntity.ticker = 20;
if (creakingHeartBlockEntity.creaking != null) {
if (CreakingHeartBlock.canSummonCreaking(level) && !(creakingHeartBlockEntity.distanceToCreaking() > 34.0)) {
if (creakingHeartBlockEntity.creaking.isRemoved()) {
creakingHeartBlockEntity.creaking = null;
}
if (!CreakingHeartBlock.hasRequiredLogs(blockState, level, blockPos) && creakingHeartBlockEntity.creaking == null) {
level.setBlock(blockPos, blockState.setValue(CreakingHeartBlock.CREAKING, CreakingHeartBlock.CreakingHeartState.DISABLED), 3);
}
} else {
creakingHeartBlockEntity.removeProtector(null);
}
} else if (!CreakingHeartBlock.hasRequiredLogs(blockState, level, blockPos)) {
level.setBlock(blockPos, blockState.setValue(CreakingHeartBlock.CREAKING, CreakingHeartBlock.CreakingHeartState.DISABLED), 3);
} else {
if (!CreakingHeartBlock.canSummonCreaking(level)) {
if (blockState.getValue(CreakingHeartBlock.CREAKING) == CreakingHeartBlock.CreakingHeartState.ACTIVE) {
level.setBlock(blockPos, blockState.setValue(CreakingHeartBlock.CREAKING, CreakingHeartBlock.CreakingHeartState.DORMANT), 3);
return;
}
} else if (blockState.getValue(CreakingHeartBlock.CREAKING) == CreakingHeartBlock.CreakingHeartState.DORMANT) {
level.setBlock(blockPos, blockState.setValue(CreakingHeartBlock.CREAKING, CreakingHeartBlock.CreakingHeartState.ACTIVE), 3);
return;
}
if (blockState.getValue(CreakingHeartBlock.CREAKING) == CreakingHeartBlock.CreakingHeartState.ACTIVE) {
if (level.getDifficulty() != Difficulty.PEACEFUL) {
if (!(level instanceof ServerLevel serverLevel && !serverLevel.getGameRules().getBoolean(GameRules.RULE_DOMOBSPAWNING))) {
Player player = level.getNearestPlayer(blockPos.getX(), blockPos.getY(), blockPos.getZ(), 32.0, false);
if (player != null) {
creakingHeartBlockEntity.creaking = spawnProtector((ServerLevel)level, creakingHeartBlockEntity);
if (creakingHeartBlockEntity.creaking != null) {
creakingHeartBlockEntity.creaking.makeSound(SoundEvents.CREAKING_SPAWN);
level.playSound(null, creakingHeartBlockEntity.getBlockPos(), SoundEvents.CREAKING_HEART_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F);
}
}
}
}
}
}
}
}
private double distanceToCreaking() {
return this.creaking == null ? 0.0 : Math.sqrt(this.creaking.distanceToSqr(Vec3.atBottomCenterOf(this.getBlockPos())));
}
@Nullable
private static CreakingTransient spawnProtector(ServerLevel serverLevel, CreakingHeartBlockEntity creakingHeartBlockEntity) {
BlockPos blockPos = creakingHeartBlockEntity.getBlockPos();
Optional<CreakingTransient> optional = SpawnUtil.trySpawnMob(
EntityType.CREAKING_TRANSIENT, EntitySpawnReason.SPAWNER, serverLevel, blockPos, 5, 16, 8, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER_NO_LEAVES
);
if (optional.isEmpty()) {
return null;
} else {
CreakingTransient creakingTransient = (CreakingTransient)optional.get();
serverLevel.gameEvent(creakingTransient, GameEvent.ENTITY_PLACE, creakingTransient.position());
serverLevel.broadcastEntityEvent(creakingTransient, (byte)60);
creakingTransient.bindToCreakingHeart(blockPos);
return creakingTransient;
}
}
public ClientboundBlockEntityDataPacket getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return this.saveCustomOnly(registries);
}
public void creakingHurt() {
if (this.creaking != null) {
if (this.level instanceof ServerLevel serverLevel) {
this.emitParticles(serverLevel, 20, false);
this.emitter = 100;
this.emitterTarget = this.creaking.getBoundingBox().getCenter();
}
}
}
private void emitParticles(ServerLevel serverLevel, int i, boolean bl) {
if (this.creaking != null) {
int j = bl ? 16545810 : 6250335;
RandomSource randomSource = serverLevel.random;
for (double d = 0.0; d < i; d++) {
Vec3 vec3 = this.creaking
.getBoundingBox()
.getMinPosition()
.add(
randomSource.nextDouble() * this.creaking.getBoundingBox().getXsize(),
randomSource.nextDouble() * this.creaking.getBoundingBox().getYsize(),
randomSource.nextDouble() * this.creaking.getBoundingBox().getZsize()
);
Vec3 vec32 = Vec3.atLowerCornerOf(this.getBlockPos()).add(randomSource.nextDouble(), randomSource.nextDouble(), randomSource.nextDouble());
if (bl) {
Vec3 vec33 = vec3;
vec3 = vec32;
vec32 = vec33;
}
TargetColorParticleOption targetColorParticleOption = new TargetColorParticleOption(vec32, j);
serverLevel.sendParticles(targetColorParticleOption, vec3.x, vec3.y, vec3.z, 1, 0.0, 0.0, 0.0, 0.0);
}
}
}
public void removeProtector(@Nullable DamageSource damageSource) {
if (this.creaking != null) {
this.creaking.tearDown(damageSource);
this.creaking = null;
}
}
public boolean isProtector(Creaking creaking) {
return this.creaking == creaking;
}
public int getAnalogOutputSignal() {
return this.outputSignal;
}
public int computeAnalogOutputSignal() {
if (this.creaking == null) {
return 0;
} else {
double d = this.distanceToCreaking();
double e = Math.clamp(d, 0.0, 32.0) / 32.0;
return 15 - (int)Math.floor(e * 15.0);
}
}
}