350 lines
17 KiB
Java
350 lines
17 KiB
Java
package net.minecraft.world.level.block.entity.vault;
|
|
|
|
import com.google.common.annotations.VisibleForTesting;
|
|
import com.mojang.serialization.DynamicOps;
|
|
import java.util.List;
|
|
import java.util.Set;
|
|
import java.util.UUID;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.Util;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.Direction;
|
|
import net.minecraft.core.HolderLookup;
|
|
import net.minecraft.core.particles.ParticleOptions;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtOps;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
|
|
import net.minecraft.resources.RegistryOps;
|
|
import net.minecraft.resources.ResourceKey;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvent;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.stats.Stats;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.util.RandomSource;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.VaultBlock;
|
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.storage.loot.LootParams;
|
|
import net.minecraft.world.level.storage.loot.LootTable;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
|
|
import net.minecraft.world.level.storage.loot.parameters.LootContextParams;
|
|
import net.minecraft.world.phys.Vec3;
|
|
|
|
public class VaultBlockEntity extends BlockEntity {
|
|
private final VaultServerData serverData = new VaultServerData();
|
|
private final VaultSharedData sharedData = new VaultSharedData();
|
|
private final VaultClientData clientData = new VaultClientData();
|
|
private VaultConfig config = VaultConfig.DEFAULT;
|
|
|
|
public VaultBlockEntity(BlockPos p_329814_, BlockState p_335937_) {
|
|
super(BlockEntityType.VAULT, p_329814_, p_335937_);
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Packet<ClientGamePacketListener> getUpdatePacket() {
|
|
return ClientboundBlockEntityDataPacket.create(this);
|
|
}
|
|
|
|
@Override
|
|
public CompoundTag getUpdateTag(HolderLookup.Provider p_335952_) {
|
|
return Util.make(
|
|
new CompoundTag(),
|
|
p_391005_ -> p_391005_.store("shared_data", VaultSharedData.CODEC, p_335952_.createSerializationContext(NbtOps.INSTANCE), this.sharedData)
|
|
);
|
|
}
|
|
|
|
@Override
|
|
protected void saveAdditional(CompoundTag p_335237_, HolderLookup.Provider p_332605_) {
|
|
super.saveAdditional(p_335237_, p_332605_);
|
|
RegistryOps<Tag> registryops = p_332605_.createSerializationContext(NbtOps.INSTANCE);
|
|
p_335237_.store("config", VaultConfig.CODEC, registryops, this.config);
|
|
p_335237_.store("shared_data", VaultSharedData.CODEC, registryops, this.sharedData);
|
|
p_335237_.store("server_data", VaultServerData.CODEC, registryops, this.serverData);
|
|
}
|
|
|
|
@Override
|
|
protected void loadAdditional(CompoundTag p_329069_, HolderLookup.Provider p_335999_) {
|
|
super.loadAdditional(p_329069_, p_335999_);
|
|
DynamicOps<Tag> dynamicops = p_335999_.createSerializationContext(NbtOps.INSTANCE);
|
|
p_329069_.read("server_data", VaultServerData.CODEC, dynamicops).ifPresent(this.serverData::set);
|
|
this.config = p_329069_.read("config", VaultConfig.CODEC, dynamicops).orElse(VaultConfig.DEFAULT);
|
|
p_329069_.read("shared_data", VaultSharedData.CODEC, dynamicops).ifPresent(this.sharedData::set);
|
|
}
|
|
|
|
@Nullable
|
|
public VaultServerData getServerData() {
|
|
return this.level != null && !this.level.isClientSide ? this.serverData : null;
|
|
}
|
|
|
|
public VaultSharedData getSharedData() {
|
|
return this.sharedData;
|
|
}
|
|
|
|
public VaultClientData getClientData() {
|
|
return this.clientData;
|
|
}
|
|
|
|
public VaultConfig getConfig() {
|
|
return this.config;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void setConfig(VaultConfig p_332483_) {
|
|
this.config = p_332483_;
|
|
}
|
|
|
|
public static final class Client {
|
|
private static final int PARTICLE_TICK_RATE = 20;
|
|
private static final float IDLE_PARTICLE_CHANCE = 0.5F;
|
|
private static final float AMBIENT_SOUND_CHANCE = 0.02F;
|
|
private static final int ACTIVATION_PARTICLE_COUNT = 20;
|
|
private static final int DEACTIVATION_PARTICLE_COUNT = 20;
|
|
|
|
public static void tick(Level p_331255_, BlockPos p_335715_, BlockState p_330773_, VaultClientData p_335986_, VaultSharedData p_333339_) {
|
|
p_335986_.updateDisplayItemSpin();
|
|
if (p_331255_.getGameTime() % 20L == 0L) {
|
|
emitConnectionParticlesForNearbyPlayers(p_331255_, p_335715_, p_330773_, p_333339_);
|
|
}
|
|
|
|
emitIdleParticles(p_331255_, p_335715_, p_333339_, p_330773_.getValue(VaultBlock.OMINOUS) ? ParticleTypes.SOUL_FIRE_FLAME : ParticleTypes.SMALL_FLAME);
|
|
playIdleSounds(p_331255_, p_335715_, p_333339_);
|
|
}
|
|
|
|
public static void emitActivationParticles(Level p_329048_, BlockPos p_334504_, BlockState p_328465_, VaultSharedData p_331322_, ParticleOptions p_332937_) {
|
|
emitConnectionParticlesForNearbyPlayers(p_329048_, p_334504_, p_328465_, p_331322_);
|
|
RandomSource randomsource = p_329048_.random;
|
|
|
|
for (int i = 0; i < 20; i++) {
|
|
Vec3 vec3 = randomPosInsideCage(p_334504_, randomsource);
|
|
p_329048_.addParticle(ParticleTypes.SMOKE, vec3.x(), vec3.y(), vec3.z(), 0.0, 0.0, 0.0);
|
|
p_329048_.addParticle(p_332937_, vec3.x(), vec3.y(), vec3.z(), 0.0, 0.0, 0.0);
|
|
}
|
|
}
|
|
|
|
public static void emitDeactivationParticles(Level p_330549_, BlockPos p_334754_, ParticleOptions p_335199_) {
|
|
RandomSource randomsource = p_330549_.random;
|
|
|
|
for (int i = 0; i < 20; i++) {
|
|
Vec3 vec3 = randomPosCenterOfCage(p_334754_, randomsource);
|
|
Vec3 vec31 = new Vec3(randomsource.nextGaussian() * 0.02, randomsource.nextGaussian() * 0.02, randomsource.nextGaussian() * 0.02);
|
|
p_330549_.addParticle(p_335199_, vec3.x(), vec3.y(), vec3.z(), vec31.x(), vec31.y(), vec31.z());
|
|
}
|
|
}
|
|
|
|
private static void emitIdleParticles(Level p_329901_, BlockPos p_330744_, VaultSharedData p_332348_, ParticleOptions p_333563_) {
|
|
RandomSource randomsource = p_329901_.getRandom();
|
|
if (randomsource.nextFloat() <= 0.5F) {
|
|
Vec3 vec3 = randomPosInsideCage(p_330744_, randomsource);
|
|
p_329901_.addParticle(ParticleTypes.SMOKE, vec3.x(), vec3.y(), vec3.z(), 0.0, 0.0, 0.0);
|
|
if (shouldDisplayActiveEffects(p_332348_)) {
|
|
p_329901_.addParticle(p_333563_, vec3.x(), vec3.y(), vec3.z(), 0.0, 0.0, 0.0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private static void emitConnectionParticlesForPlayer(Level p_327765_, Vec3 p_335116_, Player p_333131_) {
|
|
RandomSource randomsource = p_327765_.random;
|
|
Vec3 vec3 = p_335116_.vectorTo(p_333131_.position().add(0.0, p_333131_.getBbHeight() / 2.0F, 0.0));
|
|
int i = Mth.nextInt(randomsource, 2, 5);
|
|
|
|
for (int j = 0; j < i; j++) {
|
|
Vec3 vec31 = vec3.offsetRandom(randomsource, 1.0F);
|
|
p_327765_.addParticle(
|
|
ParticleTypes.VAULT_CONNECTION, p_335116_.x(), p_335116_.y(), p_335116_.z(), vec31.x(), vec31.y(), vec31.z()
|
|
);
|
|
}
|
|
}
|
|
|
|
private static void emitConnectionParticlesForNearbyPlayers(Level p_329933_, BlockPos p_335364_, BlockState p_330110_, VaultSharedData p_332177_) {
|
|
Set<UUID> set = p_332177_.getConnectedPlayers();
|
|
if (!set.isEmpty()) {
|
|
Vec3 vec3 = keyholePos(p_335364_, p_330110_.getValue(VaultBlock.FACING));
|
|
|
|
for (UUID uuid : set) {
|
|
Player player = p_329933_.getPlayerByUUID(uuid);
|
|
if (player != null && isWithinConnectionRange(p_335364_, p_332177_, player)) {
|
|
emitConnectionParticlesForPlayer(p_329933_, vec3, player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static boolean isWithinConnectionRange(BlockPos p_334746_, VaultSharedData p_334927_, Player p_333038_) {
|
|
return p_333038_.blockPosition().distSqr(p_334746_) <= Mth.square(p_334927_.connectedParticlesRange());
|
|
}
|
|
|
|
private static void playIdleSounds(Level p_329850_, BlockPos p_333501_, VaultSharedData p_332082_) {
|
|
if (shouldDisplayActiveEffects(p_332082_)) {
|
|
RandomSource randomsource = p_329850_.getRandom();
|
|
if (randomsource.nextFloat() <= 0.02F) {
|
|
p_329850_.playLocalSound(
|
|
p_333501_, SoundEvents.VAULT_AMBIENT, SoundSource.BLOCKS, randomsource.nextFloat() * 0.25F + 0.75F, randomsource.nextFloat() + 0.5F, false
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static boolean shouldDisplayActiveEffects(VaultSharedData p_329617_) {
|
|
return p_329617_.hasDisplayItem();
|
|
}
|
|
|
|
private static Vec3 randomPosCenterOfCage(BlockPos p_329856_, RandomSource p_333945_) {
|
|
return Vec3.atLowerCornerOf(p_329856_)
|
|
.add(Mth.nextDouble(p_333945_, 0.4, 0.6), Mth.nextDouble(p_333945_, 0.4, 0.6), Mth.nextDouble(p_333945_, 0.4, 0.6));
|
|
}
|
|
|
|
private static Vec3 randomPosInsideCage(BlockPos p_327884_, RandomSource p_332986_) {
|
|
return Vec3.atLowerCornerOf(p_327884_)
|
|
.add(Mth.nextDouble(p_332986_, 0.1, 0.9), Mth.nextDouble(p_332986_, 0.25, 0.75), Mth.nextDouble(p_332986_, 0.1, 0.9));
|
|
}
|
|
|
|
private static Vec3 keyholePos(BlockPos p_331540_, Direction p_333034_) {
|
|
return Vec3.atBottomCenterOf(p_331540_).add(p_333034_.getStepX() * 0.5, 1.75, p_333034_.getStepZ() * 0.5);
|
|
}
|
|
}
|
|
|
|
public static final class Server {
|
|
private static final int UNLOCKING_DELAY_TICKS = 14;
|
|
private static final int DISPLAY_CYCLE_TICK_RATE = 20;
|
|
private static final int INSERT_FAIL_SOUND_BUFFER_TICKS = 15;
|
|
|
|
public static void tick(
|
|
ServerLevel p_327862_, BlockPos p_334036_, BlockState p_336094_, VaultConfig p_332912_, VaultServerData p_332613_, VaultSharedData p_336360_
|
|
) {
|
|
VaultState vaultstate = p_336094_.getValue(VaultBlock.STATE);
|
|
if (shouldCycleDisplayItem(p_327862_.getGameTime(), vaultstate)) {
|
|
cycleDisplayItemFromLootTable(p_327862_, vaultstate, p_332912_, p_336360_, p_334036_);
|
|
}
|
|
|
|
BlockState blockstate = p_336094_;
|
|
if (p_327862_.getGameTime() >= p_332613_.stateUpdatingResumesAt()) {
|
|
blockstate = p_336094_.setValue(VaultBlock.STATE, vaultstate.tickAndGetNext(p_327862_, p_334036_, p_332912_, p_332613_, p_336360_));
|
|
if (p_336094_ != blockstate) {
|
|
setVaultState(p_327862_, p_334036_, p_336094_, blockstate, p_332912_, p_336360_);
|
|
}
|
|
}
|
|
|
|
if (p_332613_.isDirty || p_336360_.isDirty) {
|
|
VaultBlockEntity.setChanged(p_327862_, p_334036_, p_336094_);
|
|
if (p_336360_.isDirty) {
|
|
p_327862_.sendBlockUpdated(p_334036_, p_336094_, blockstate, 2);
|
|
}
|
|
|
|
p_332613_.isDirty = false;
|
|
p_336360_.isDirty = false;
|
|
}
|
|
}
|
|
|
|
public static void tryInsertKey(
|
|
ServerLevel p_330813_,
|
|
BlockPos p_333223_,
|
|
BlockState p_331301_,
|
|
VaultConfig p_333877_,
|
|
VaultServerData p_334388_,
|
|
VaultSharedData p_330336_,
|
|
Player p_332764_,
|
|
ItemStack p_329896_
|
|
) {
|
|
VaultState vaultstate = p_331301_.getValue(VaultBlock.STATE);
|
|
if (canEjectReward(p_333877_, vaultstate)) {
|
|
if (!isValidToInsert(p_333877_, p_329896_)) {
|
|
playInsertFailSound(p_330813_, p_334388_, p_333223_, SoundEvents.VAULT_INSERT_ITEM_FAIL);
|
|
} else if (p_334388_.hasRewardedPlayer(p_332764_)) {
|
|
playInsertFailSound(p_330813_, p_334388_, p_333223_, SoundEvents.VAULT_REJECT_REWARDED_PLAYER);
|
|
} else {
|
|
List<ItemStack> list = resolveItemsToEject(p_330813_, p_333877_, p_333223_, p_332764_, p_329896_);
|
|
if (!list.isEmpty()) {
|
|
p_332764_.awardStat(Stats.ITEM_USED.get(p_329896_.getItem()));
|
|
p_329896_.consume(p_333877_.keyItem().getCount(), p_332764_);
|
|
unlock(p_330813_, p_331301_, p_333223_, p_333877_, p_334388_, p_330336_, list);
|
|
p_334388_.addToRewardedPlayers(p_332764_);
|
|
p_330336_.updateConnectedPlayersWithinRange(p_330813_, p_333223_, p_334388_, p_333877_, p_333877_.deactivationRange());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void setVaultState(
|
|
ServerLevel p_327709_, BlockPos p_330897_, BlockState p_333801_, BlockState p_336357_, VaultConfig p_332945_, VaultSharedData p_328872_
|
|
) {
|
|
VaultState vaultstate = p_333801_.getValue(VaultBlock.STATE);
|
|
VaultState vaultstate1 = p_336357_.getValue(VaultBlock.STATE);
|
|
p_327709_.setBlock(p_330897_, p_336357_, 3);
|
|
vaultstate.onTransition(p_327709_, p_330897_, vaultstate1, p_332945_, p_328872_, p_336357_.getValue(VaultBlock.OMINOUS));
|
|
}
|
|
|
|
static void cycleDisplayItemFromLootTable(ServerLevel p_328186_, VaultState p_335064_, VaultConfig p_329242_, VaultSharedData p_336318_, BlockPos p_327920_) {
|
|
if (!canEjectReward(p_329242_, p_335064_)) {
|
|
p_336318_.setDisplayItem(ItemStack.EMPTY);
|
|
} else {
|
|
ItemStack itemstack = getRandomDisplayItemFromLootTable(p_328186_, p_327920_, p_329242_.overrideLootTableToDisplay().orElse(p_329242_.lootTable()));
|
|
p_336318_.setDisplayItem(itemstack);
|
|
}
|
|
}
|
|
|
|
private static ItemStack getRandomDisplayItemFromLootTable(ServerLevel p_329309_, BlockPos p_331772_, ResourceKey<LootTable> p_327947_) {
|
|
LootTable loottable = p_329309_.getServer().reloadableRegistries().getLootTable(p_327947_);
|
|
LootParams lootparams = new LootParams.Builder(p_329309_)
|
|
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(p_331772_))
|
|
.create(LootContextParamSets.VAULT);
|
|
List<ItemStack> list = loottable.getRandomItems(lootparams, p_329309_.getRandom());
|
|
return list.isEmpty() ? ItemStack.EMPTY : Util.getRandom(list, p_329309_.getRandom());
|
|
}
|
|
|
|
private static void unlock(
|
|
ServerLevel p_329025_,
|
|
BlockState p_334542_,
|
|
BlockPos p_331457_,
|
|
VaultConfig p_328759_,
|
|
VaultServerData p_329258_,
|
|
VaultSharedData p_328090_,
|
|
List<ItemStack> p_328105_
|
|
) {
|
|
p_329258_.setItemsToEject(p_328105_);
|
|
p_328090_.setDisplayItem(p_329258_.getNextItemToEject());
|
|
p_329258_.pauseStateUpdatingUntil(p_329025_.getGameTime() + 14L);
|
|
setVaultState(p_329025_, p_331457_, p_334542_, p_334542_.setValue(VaultBlock.STATE, VaultState.UNLOCKING), p_328759_, p_328090_);
|
|
}
|
|
|
|
private static List<ItemStack> resolveItemsToEject(ServerLevel p_332295_, VaultConfig p_329503_, BlockPos p_333443_, Player p_334837_, ItemStack p_369229_) {
|
|
LootTable loottable = p_332295_.getServer().reloadableRegistries().getLootTable(p_329503_.lootTable());
|
|
LootParams lootparams = new LootParams.Builder(p_332295_)
|
|
.withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(p_333443_))
|
|
.withLuck(p_334837_.getLuck())
|
|
.withParameter(LootContextParams.THIS_ENTITY, p_334837_)
|
|
.withParameter(LootContextParams.TOOL, p_369229_)
|
|
.create(LootContextParamSets.VAULT);
|
|
return loottable.getRandomItems(lootparams);
|
|
}
|
|
|
|
private static boolean canEjectReward(VaultConfig p_333220_, VaultState p_335172_) {
|
|
return !p_333220_.keyItem().isEmpty() && p_335172_ != VaultState.INACTIVE;
|
|
}
|
|
|
|
private static boolean isValidToInsert(VaultConfig p_334332_, ItemStack p_335056_) {
|
|
return ItemStack.isSameItemSameComponents(p_335056_, p_334332_.keyItem()) && p_335056_.getCount() >= p_334332_.keyItem().getCount();
|
|
}
|
|
|
|
private static boolean shouldCycleDisplayItem(long p_334702_, VaultState p_332761_) {
|
|
return p_334702_ % 20L == 0L && p_332761_ == VaultState.ACTIVE;
|
|
}
|
|
|
|
private static void playInsertFailSound(ServerLevel p_334677_, VaultServerData p_330421_, BlockPos p_330460_, SoundEvent p_342956_) {
|
|
if (p_334677_.getGameTime() >= p_330421_.getLastInsertFailTimestamp() + 15L) {
|
|
p_334677_.playSound(null, p_330460_, p_342956_, SoundSource.BLOCKS);
|
|
p_330421_.setLastInsertFailTimestamp(p_334677_.getGameTime());
|
|
}
|
|
}
|
|
}
|
|
} |