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

362 lines
15 KiB
Java
Raw Permalink Normal View History

2025-07-01 06:20:03 +00:00
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<List<Holder<MobEffect>>> 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<Holder<MobEffect>> 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<BeaconBeamOwner.Section> beamSections = new ArrayList<>();
private List<BeaconBeamOwner.Section> checkingBeamSections = new ArrayList<>();
int levels;
private int lastCheckY;
@Nullable
Holder<MobEffect> primaryPower;
@Nullable
Holder<MobEffect> 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<MobEffect> filterEffect(@Nullable Holder<MobEffect> 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<MobEffect> p_329363_, @Nullable Holder<MobEffect> 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<Player> 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<BeaconBeamOwner.Section> getBeamSections() {
return (List<BeaconBeamOwner.Section>)(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<MobEffect> 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<MobEffect> 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;
}
}