package net.minecraft.world.level.block.entity; import com.google.common.collect.ImmutableList; import com.google.common.collect.Lists; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Objects; import java.util.Set; import java.util.stream.Collectors; import javax.annotation.Nullable; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.core.Holder; import net.minecraft.core.HolderLookup; import net.minecraft.core.component.DataComponentGetter; import net.minecraft.core.component.DataComponentMap; import net.minecraft.core.component.DataComponents; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtOps; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.ComponentSerialization; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.resources.ResourceKey; 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.BlockTags; import net.minecraft.util.ARGB; import net.minecraft.world.LockCode; import net.minecraft.world.MenuProvider; import net.minecraft.world.Nameable; import net.minecraft.world.effect.MobEffect; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.inventory.BeaconMenu; import net.minecraft.world.inventory.ContainerData; import net.minecraft.world.inventory.ContainerLevelAccess; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.BeaconBeamBlock; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.Heightmap; import net.minecraft.world.phys.AABB; public class BeaconBlockEntity extends BlockEntity implements MenuProvider, Nameable, BeaconBeamOwner { private static final int MAX_LEVELS = 4; public static final List>> BEACON_EFFECTS = List.of( List.of(MobEffects.SPEED, MobEffects.HASTE), List.of(MobEffects.RESISTANCE, MobEffects.JUMP_BOOST), List.of(MobEffects.STRENGTH), List.of(MobEffects.REGENERATION) ); private static final Set> VALID_EFFECTS = BEACON_EFFECTS.stream().flatMap(Collection::stream).collect(Collectors.toSet()); public static final int DATA_LEVELS = 0; public static final int DATA_PRIMARY = 1; public static final int DATA_SECONDARY = 2; public static final int NUM_DATA_VALUES = 3; private static final int BLOCKS_CHECK_PER_TICK = 10; private static final Component DEFAULT_NAME = Component.translatable("container.beacon"); private static final String TAG_PRIMARY = "primary_effect"; private static final String TAG_SECONDARY = "secondary_effect"; List beamSections = new ArrayList<>(); private List checkingBeamSections = new ArrayList<>(); int levels; private int lastCheckY; @Nullable Holder primaryPower; @Nullable Holder secondaryPower; @Nullable private Component name; private LockCode lockKey = LockCode.NO_LOCK; private final ContainerData dataAccess = new ContainerData() { @Override public int get(int p_58711_) { return switch (p_58711_) { case 0 -> BeaconBlockEntity.this.levels; case 1 -> BeaconMenu.encodeEffect(BeaconBlockEntity.this.primaryPower); case 2 -> BeaconMenu.encodeEffect(BeaconBlockEntity.this.secondaryPower); default -> 0; }; } @Override public void set(int p_58713_, int p_58714_) { switch (p_58713_) { case 0: BeaconBlockEntity.this.levels = p_58714_; break; case 1: if (!BeaconBlockEntity.this.level.isClientSide && !BeaconBlockEntity.this.beamSections.isEmpty()) { BeaconBlockEntity.playSound(BeaconBlockEntity.this.level, BeaconBlockEntity.this.worldPosition, SoundEvents.BEACON_POWER_SELECT); } BeaconBlockEntity.this.primaryPower = BeaconBlockEntity.filterEffect(BeaconMenu.decodeEffect(p_58714_)); break; case 2: BeaconBlockEntity.this.secondaryPower = BeaconBlockEntity.filterEffect(BeaconMenu.decodeEffect(p_58714_)); } } @Override public int getCount() { return 3; } }; @Nullable static Holder filterEffect(@Nullable Holder p_330198_) { return VALID_EFFECTS.contains(p_330198_) ? p_330198_ : null; } public BeaconBlockEntity(BlockPos p_155088_, BlockState p_155089_) { super(BlockEntityType.BEACON, p_155088_, p_155089_); } public static void tick(Level p_155108_, BlockPos p_155109_, BlockState p_155110_, BeaconBlockEntity p_155111_) { int i = p_155109_.getX(); int j = p_155109_.getY(); int k = p_155109_.getZ(); BlockPos blockpos; if (p_155111_.lastCheckY < j) { blockpos = p_155109_; p_155111_.checkingBeamSections = Lists.newArrayList(); p_155111_.lastCheckY = p_155109_.getY() - 1; } else { blockpos = new BlockPos(i, p_155111_.lastCheckY + 1, k); } BeaconBeamOwner.Section beaconbeamowner$section = p_155111_.checkingBeamSections.isEmpty() ? null : p_155111_.checkingBeamSections.get(p_155111_.checkingBeamSections.size() - 1); int l = p_155108_.getHeight(Heightmap.Types.WORLD_SURFACE, i, k); for (int i1 = 0; i1 < 10 && blockpos.getY() <= l; i1++) { BlockState blockstate = p_155108_.getBlockState(blockpos); if (blockstate.getBlock() instanceof BeaconBeamBlock beaconbeamblock) { int j1 = beaconbeamblock.getColor().getTextureDiffuseColor(); if (p_155111_.checkingBeamSections.size() <= 1) { beaconbeamowner$section = new BeaconBeamOwner.Section(j1); p_155111_.checkingBeamSections.add(beaconbeamowner$section); } else if (beaconbeamowner$section != null) { if (j1 == beaconbeamowner$section.getColor()) { beaconbeamowner$section.increaseHeight(); } else { beaconbeamowner$section = new BeaconBeamOwner.Section(ARGB.average(beaconbeamowner$section.getColor(), j1)); p_155111_.checkingBeamSections.add(beaconbeamowner$section); } } } else { if (beaconbeamowner$section == null || blockstate.getLightBlock() >= 15 && !blockstate.is(Blocks.BEDROCK)) { p_155111_.checkingBeamSections.clear(); p_155111_.lastCheckY = l; break; } beaconbeamowner$section.increaseHeight(); } blockpos = blockpos.above(); p_155111_.lastCheckY++; } int k1 = p_155111_.levels; if (p_155108_.getGameTime() % 80L == 0L) { if (!p_155111_.beamSections.isEmpty()) { p_155111_.levels = updateBase(p_155108_, i, j, k); } if (p_155111_.levels > 0 && !p_155111_.beamSections.isEmpty()) { applyEffects(p_155108_, p_155109_, p_155111_.levels, p_155111_.primaryPower, p_155111_.secondaryPower); playSound(p_155108_, p_155109_, SoundEvents.BEACON_AMBIENT); } } if (p_155111_.lastCheckY >= l) { p_155111_.lastCheckY = p_155108_.getMinY() - 1; boolean flag = k1 > 0; p_155111_.beamSections = p_155111_.checkingBeamSections; if (!p_155108_.isClientSide) { boolean flag1 = p_155111_.levels > 0; if (!flag && flag1) { playSound(p_155108_, p_155109_, SoundEvents.BEACON_ACTIVATE); for (ServerPlayer serverplayer : p_155108_.getEntitiesOfClass(ServerPlayer.class, new AABB(i, j, k, i, j - 4, k).inflate(10.0, 5.0, 10.0))) { CriteriaTriggers.CONSTRUCT_BEACON.trigger(serverplayer, p_155111_.levels); } } else if (flag && !flag1) { playSound(p_155108_, p_155109_, SoundEvents.BEACON_DEACTIVATE); } } } } private static int updateBase(Level p_155093_, int p_155094_, int p_155095_, int p_155096_) { int i = 0; for (int j = 1; j <= 4; i = j++) { int k = p_155095_ - j; if (k < p_155093_.getMinY()) { break; } boolean flag = true; for (int l = p_155094_ - j; l <= p_155094_ + j && flag; l++) { for (int i1 = p_155096_ - j; i1 <= p_155096_ + j; i1++) { if (!p_155093_.getBlockState(new BlockPos(l, k, i1)).is(BlockTags.BEACON_BASE_BLOCKS)) { flag = false; break; } } } if (!flag) { break; } } return i; } @Override public void setRemoved() { playSound(this.level, this.worldPosition, SoundEvents.BEACON_DEACTIVATE); super.setRemoved(); } private static void applyEffects( Level p_155098_, BlockPos p_155099_, int p_155100_, @Nullable Holder p_329363_, @Nullable Holder p_332048_ ) { if (!p_155098_.isClientSide && p_329363_ != null) { double d0 = p_155100_ * 10 + 10; int i = 0; if (p_155100_ >= 4 && Objects.equals(p_329363_, p_332048_)) { i = 1; } int j = (9 + p_155100_ * 2) * 20; AABB aabb = new AABB(p_155099_).inflate(d0).expandTowards(0.0, p_155098_.getHeight(), 0.0); List list = p_155098_.getEntitiesOfClass(Player.class, aabb); for (Player player : list) { player.addEffect(new MobEffectInstance(p_329363_, j, i, true, true)); } if (p_155100_ >= 4 && !Objects.equals(p_329363_, p_332048_) && p_332048_ != null) { for (Player player1 : list) { player1.addEffect(new MobEffectInstance(p_332048_, j, 0, true, true)); } } } } public static void playSound(Level p_155104_, BlockPos p_155105_, SoundEvent p_155106_) { p_155104_.playSound(null, p_155105_, p_155106_, SoundSource.BLOCKS, 1.0F, 1.0F); } @Override public List getBeamSections() { return (List)(this.levels == 0 ? ImmutableList.of() : this.beamSections); } public ClientboundBlockEntityDataPacket getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); } @Override public CompoundTag getUpdateTag(HolderLookup.Provider p_333588_) { return this.saveCustomOnly(p_333588_); } private static void storeEffect(CompoundTag p_299457_, String p_297212_, @Nullable Holder p_329692_) { if (p_329692_ != null) { p_329692_.unwrapKey().ifPresent(p_333727_ -> p_299457_.putString(p_297212_, p_333727_.location().toString())); } } @Nullable private static Holder loadEffect(CompoundTag p_298536_, String p_301201_) { return p_298536_.read(p_301201_, BuiltInRegistries.MOB_EFFECT.holderByNameCodec()).filter(VALID_EFFECTS::contains).orElse(null); } @Override protected void loadAdditional(CompoundTag p_333194_, HolderLookup.Provider p_333691_) { super.loadAdditional(p_333194_, p_333691_); this.primaryPower = loadEffect(p_333194_, "primary_effect"); this.secondaryPower = loadEffect(p_333194_, "secondary_effect"); this.name = parseCustomNameSafe(p_333194_.get("CustomName"), p_333691_); this.lockKey = LockCode.fromTag(p_333194_, p_333691_); } @Override protected void saveAdditional(CompoundTag p_187463_, HolderLookup.Provider p_330516_) { super.saveAdditional(p_187463_, p_330516_); storeEffect(p_187463_, "primary_effect", this.primaryPower); storeEffect(p_187463_, "secondary_effect", this.secondaryPower); p_187463_.putInt("Levels", this.levels); p_187463_.storeNullable("CustomName", ComponentSerialization.CODEC, p_330516_.createSerializationContext(NbtOps.INSTANCE), this.name); this.lockKey.addToTag(p_187463_, p_330516_); } public void setCustomName(@Nullable Component p_58682_) { this.name = p_58682_; } @Nullable @Override public Component getCustomName() { return this.name; } @Nullable @Override public AbstractContainerMenu createMenu(int p_58696_, Inventory p_58697_, Player p_58698_) { return BaseContainerBlockEntity.canUnlock(p_58698_, this.lockKey, this.getDisplayName()) ? new BeaconMenu(p_58696_, p_58697_, this.dataAccess, ContainerLevelAccess.create(this.level, this.getBlockPos())) : null; } @Override public Component getDisplayName() { return this.getName(); } @Override public Component getName() { return this.name != null ? this.name : DEFAULT_NAME; } @Override protected void applyImplicitComponents(DataComponentGetter p_396481_) { super.applyImplicitComponents(p_396481_); this.name = p_396481_.get(DataComponents.CUSTOM_NAME); this.lockKey = p_396481_.getOrDefault(DataComponents.LOCK, LockCode.NO_LOCK); } @Override protected void collectImplicitComponents(DataComponentMap.Builder p_329382_) { super.collectImplicitComponents(p_329382_); p_329382_.set(DataComponents.CUSTOM_NAME, this.name); if (!this.lockKey.equals(LockCode.NO_LOCK)) { p_329382_.set(DataComponents.LOCK, this.lockKey); } } @Override public void removeComponentsFromTag(CompoundTag p_331794_) { p_331794_.remove("CustomName"); p_331794_.remove("lock"); } @Override public void setLevel(Level p_155091_) { super.setLevel(p_155091_); this.lastCheckY = p_155091_.getMinY() - 1; } }