package net.minecraft.world.level.block.entity; import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import java.util.OptionalInt; import javax.annotation.Nullable; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.nbt.Tag; import net.minecraft.resources.RegistryOps; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.GameEventTags; import net.minecraft.tags.TagKey; import net.minecraft.util.Mth; import net.minecraft.util.SpawnUtil; import net.minecraft.world.Difficulty; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntitySpawnReason; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.monster.warden.Warden; import net.minecraft.world.entity.monster.warden.WardenSpawnTracker; import net.minecraft.world.entity.projectile.Projectile; import net.minecraft.world.level.GameRules; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.SculkShriekerBlock; 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.level.gameevent.vibrations.VibrationSystem; import net.minecraft.world.phys.Vec3; public class SculkShriekerBlockEntity extends BlockEntity implements GameEventListener.Provider, VibrationSystem { private static final int WARNING_SOUND_RADIUS = 10; private static final int WARDEN_SPAWN_ATTEMPTS = 20; private static final int WARDEN_SPAWN_RANGE_XZ = 5; private static final int WARDEN_SPAWN_RANGE_Y = 6; private static final int DARKNESS_RADIUS = 40; private static final int SHRIEKING_TICKS = 90; private static final Int2ObjectMap SOUND_BY_LEVEL = Util.make(new Int2ObjectOpenHashMap<>(), p_222866_ -> { p_222866_.put(1, SoundEvents.WARDEN_NEARBY_CLOSE); p_222866_.put(2, SoundEvents.WARDEN_NEARBY_CLOSER); p_222866_.put(3, SoundEvents.WARDEN_NEARBY_CLOSEST); p_222866_.put(4, SoundEvents.WARDEN_LISTENING_ANGRY); }); private static final int DEFAULT_WARNING_LEVEL = 0; private int warningLevel = 0; private final VibrationSystem.User vibrationUser = new SculkShriekerBlockEntity.VibrationUser(); private VibrationSystem.Data vibrationData = new VibrationSystem.Data(); private final VibrationSystem.Listener vibrationListener = new VibrationSystem.Listener(this); public SculkShriekerBlockEntity(BlockPos p_222835_, BlockState p_222836_) { super(BlockEntityType.SCULK_SHRIEKER, p_222835_, p_222836_); } @Override public VibrationSystem.Data getVibrationData() { return this.vibrationData; } @Override public VibrationSystem.User getVibrationUser() { return this.vibrationUser; } @Override protected void loadAdditional(CompoundTag p_327746_, HolderLookup.Provider p_335650_) { super.loadAdditional(p_327746_, p_335650_); this.warningLevel = p_327746_.getIntOr("warning_level", 0); RegistryOps registryops = p_335650_.createSerializationContext(NbtOps.INSTANCE); this.vibrationData = p_327746_.read("listener", VibrationSystem.Data.CODEC, registryops).orElseGet(VibrationSystem.Data::new); } @Override protected void saveAdditional(CompoundTag p_222878_, HolderLookup.Provider p_330845_) { super.saveAdditional(p_222878_, p_330845_); p_222878_.putInt("warning_level", this.warningLevel); RegistryOps registryops = p_330845_.createSerializationContext(NbtOps.INSTANCE); p_222878_.store("listener", VibrationSystem.Data.CODEC, registryops, this.vibrationData); } @Nullable public static ServerPlayer tryGetPlayer(@Nullable Entity p_222862_) { if (p_222862_ instanceof ServerPlayer serverplayer1) { return serverplayer1; } else if (p_222862_ != null && p_222862_.getControllingPassenger() instanceof ServerPlayer serverplayer) { return serverplayer; } else if (p_222862_ instanceof Projectile projectile && projectile.getOwner() instanceof ServerPlayer serverplayer3) { return serverplayer3; } else { return p_222862_ instanceof ItemEntity itementity && itementity.getOwner() instanceof ServerPlayer serverplayer2 ? serverplayer2 : null; } } public void tryShriek(ServerLevel p_222842_, @Nullable ServerPlayer p_222843_) { if (p_222843_ != null) { BlockState blockstate = this.getBlockState(); if (!blockstate.getValue(SculkShriekerBlock.SHRIEKING)) { this.warningLevel = 0; if (!this.canRespond(p_222842_) || this.tryToWarn(p_222842_, p_222843_)) { this.shriek(p_222842_, p_222843_); } } } } private boolean tryToWarn(ServerLevel p_222875_, ServerPlayer p_222876_) { OptionalInt optionalint = WardenSpawnTracker.tryWarn(p_222875_, this.getBlockPos(), p_222876_); optionalint.ifPresent(p_222838_ -> this.warningLevel = p_222838_); return optionalint.isPresent(); } private void shriek(ServerLevel p_222845_, @Nullable Entity p_222846_) { BlockPos blockpos = this.getBlockPos(); BlockState blockstate = this.getBlockState(); p_222845_.setBlock(blockpos, blockstate.setValue(SculkShriekerBlock.SHRIEKING, true), 2); p_222845_.scheduleTick(blockpos, blockstate.getBlock(), 90); p_222845_.levelEvent(3007, blockpos, 0); p_222845_.gameEvent(GameEvent.SHRIEK, blockpos, GameEvent.Context.of(p_222846_)); } private boolean canRespond(ServerLevel p_222873_) { return this.getBlockState().getValue(SculkShriekerBlock.CAN_SUMMON) && p_222873_.getDifficulty() != Difficulty.PEACEFUL && p_222873_.getGameRules().getBoolean(GameRules.RULE_DO_WARDEN_SPAWNING); } @Override public void preRemoveSideEffects(BlockPos p_392349_, BlockState p_398015_) { if (p_398015_.getValue(SculkShriekerBlock.SHRIEKING) && this.level instanceof ServerLevel serverlevel) { this.tryRespond(serverlevel); } } public void tryRespond(ServerLevel p_222840_) { if (this.canRespond(p_222840_) && this.warningLevel > 0) { if (!this.trySummonWarden(p_222840_)) { this.playWardenReplySound(p_222840_); } Warden.applyDarknessAround(p_222840_, Vec3.atCenterOf(this.getBlockPos()), null, 40); } } private void playWardenReplySound(Level p_281300_) { SoundEvent soundevent = SOUND_BY_LEVEL.get(this.warningLevel); if (soundevent != null) { BlockPos blockpos = this.getBlockPos(); int i = blockpos.getX() + Mth.randomBetweenInclusive(p_281300_.random, -10, 10); int j = blockpos.getY() + Mth.randomBetweenInclusive(p_281300_.random, -10, 10); int k = blockpos.getZ() + Mth.randomBetweenInclusive(p_281300_.random, -10, 10); p_281300_.playSound(null, i, j, k, soundevent, SoundSource.HOSTILE, 5.0F, 1.0F); } } private boolean trySummonWarden(ServerLevel p_222881_) { return this.warningLevel < 4 ? false : SpawnUtil.trySpawnMob(EntityType.WARDEN, EntitySpawnReason.TRIGGERED, p_222881_, this.getBlockPos(), 20, 5, 6, SpawnUtil.Strategy.ON_TOP_OF_COLLIDER, false) .isPresent(); } public VibrationSystem.Listener getListener() { return this.vibrationListener; } class VibrationUser implements VibrationSystem.User { private static final int LISTENER_RADIUS = 8; private final PositionSource positionSource = new BlockPositionSource(SculkShriekerBlockEntity.this.worldPosition); public VibrationUser() { } @Override public int getListenerRadius() { return 8; } @Override public PositionSource getPositionSource() { return this.positionSource; } @Override public TagKey getListenableEvents() { return GameEventTags.SHRIEKER_CAN_LISTEN; } @Override public boolean canReceiveVibration(ServerLevel p_281256_, BlockPos p_281528_, Holder p_335342_, GameEvent.Context p_282914_) { return !SculkShriekerBlockEntity.this.getBlockState().getValue(SculkShriekerBlock.SHRIEKING) && SculkShriekerBlockEntity.tryGetPlayer(p_282914_.sourceEntity()) != null; } @Override public void onReceiveVibration( ServerLevel p_283372_, BlockPos p_281679_, Holder p_330622_, @Nullable Entity p_282286_, @Nullable Entity p_281384_, float p_283119_ ) { SculkShriekerBlockEntity.this.tryShriek(p_283372_, SculkShriekerBlockEntity.tryGetPlayer(p_281384_ != null ? p_281384_ : p_282286_)); } @Override public void onDataChanged() { SculkShriekerBlockEntity.this.setChanged(); } @Override public boolean requiresAdjacentChunksToBeTicking() { return true; } } }