package net.minecraft.world.level.block.entity; import com.google.common.annotations.VisibleForTesting; import net.minecraft.Optionull; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SculkCatalystBlock; import net.minecraft.world.level.block.SculkSpreader; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.gameevent.BlockPositionSource; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.gameevent.GameEventListener; import net.minecraft.world.level.gameevent.PositionSource; import net.minecraft.world.phys.Vec3; public class SculkCatalystBlockEntity extends BlockEntity implements GameEventListener.Provider { private final SculkCatalystBlockEntity.CatalystListener catalystListener; public SculkCatalystBlockEntity(BlockPos p_222774_, BlockState p_222775_) { super(BlockEntityType.SCULK_CATALYST, p_222774_, p_222775_); this.catalystListener = new SculkCatalystBlockEntity.CatalystListener(p_222775_, new BlockPositionSource(p_222774_)); } public static void serverTick(Level p_222780_, BlockPos p_222781_, BlockState p_222782_, SculkCatalystBlockEntity p_222783_) { p_222783_.catalystListener.getSculkSpreader().updateCursors(p_222780_, p_222781_, p_222780_.getRandom(), true); } @Override protected void loadAdditional(CompoundTag p_334885_, HolderLookup.Provider p_332157_) { super.loadAdditional(p_334885_, p_332157_); this.catalystListener.sculkSpreader.load(p_334885_); } @Override protected void saveAdditional(CompoundTag p_222789_, HolderLookup.Provider p_332461_) { this.catalystListener.sculkSpreader.save(p_222789_); super.saveAdditional(p_222789_, p_332461_); } public SculkCatalystBlockEntity.CatalystListener getListener() { return this.catalystListener; } public static class CatalystListener implements GameEventListener { public static final int PULSE_TICKS = 8; final SculkSpreader sculkSpreader; private final BlockState blockState; private final PositionSource positionSource; public CatalystListener(BlockState p_283224_, PositionSource p_283095_) { this.blockState = p_283224_; this.positionSource = p_283095_; this.sculkSpreader = SculkSpreader.createLevelSpreader(); } @Override public PositionSource getListenerSource() { return this.positionSource; } @Override public int getListenerRadius() { return 8; } @Override public GameEventListener.DeliveryMode getDeliveryMode() { return GameEventListener.DeliveryMode.BY_DISTANCE; } @Override public boolean handleGameEvent(ServerLevel p_283470_, Holder p_332335_, GameEvent.Context p_283014_, Vec3 p_282350_) { if (p_332335_.is(GameEvent.ENTITY_DIE) && p_283014_.sourceEntity() instanceof LivingEntity livingentity) { if (!livingentity.wasExperienceConsumed()) { DamageSource damagesource = livingentity.getLastDamageSource(); int i = livingentity.getExperienceReward(p_283470_, Optionull.map(damagesource, DamageSource::getEntity)); if (livingentity.shouldDropExperience() && i > 0) { this.sculkSpreader.addCursors(BlockPos.containing(p_282350_.relative(Direction.UP, 0.5)), i); this.tryAwardItSpreadsAdvancement(p_283470_, livingentity); } livingentity.skipDropExperience(); this.positionSource .getPosition(p_283470_) .ifPresent(p_390969_ -> this.bloom(p_283470_, BlockPos.containing(p_390969_), this.blockState, p_283470_.getRandom())); } return true; } else { return false; } } @VisibleForTesting public SculkSpreader getSculkSpreader() { return this.sculkSpreader; } private void bloom(ServerLevel p_281501_, BlockPos p_281448_, BlockState p_281966_, RandomSource p_283606_) { p_281501_.setBlock(p_281448_, p_281966_.setValue(SculkCatalystBlock.PULSE, true), 3); p_281501_.scheduleTick(p_281448_, p_281966_.getBlock(), 8); p_281501_.sendParticles( ParticleTypes.SCULK_SOUL, p_281448_.getX() + 0.5, p_281448_.getY() + 1.15, p_281448_.getZ() + 0.5, 2, 0.2, 0.0, 0.2, 0.0 ); p_281501_.playSound(null, p_281448_, SoundEvents.SCULK_CATALYST_BLOOM, SoundSource.BLOCKS, 2.0F, 0.6F + p_283606_.nextFloat() * 0.4F); } private void tryAwardItSpreadsAdvancement(Level p_281279_, LivingEntity p_281378_) { if (p_281378_.getLastHurtByMob() instanceof ServerPlayer serverplayer) { DamageSource damagesource = p_281378_.getLastDamageSource() == null ? p_281279_.damageSources().playerAttack(serverplayer) : p_281378_.getLastDamageSource(); CriteriaTriggers.KILL_MOB_NEAR_SCULK_CATALYST.trigger(serverplayer, p_281378_, damagesource); } } } }