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 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); } } }