Code/net/minecraft/world/level/block/entity/SculkShriekerBlockEntity.java

222 lines
9.6 KiB
Java

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.Listener>, 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<SoundEvent> 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<Tag> 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<Tag> 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<GameEvent> getListenableEvents() {
return GameEventTags.SHRIEKER_CAN_LISTEN;
}
@Override
public boolean canReceiveVibration(ServerLevel p_281256_, BlockPos p_281528_, Holder<GameEvent> 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<GameEvent> 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;
}
}
}