package net.minecraft.world.level.block; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.mojang.serialization.MapCodec; import java.util.Optional; import javax.annotation.Nullable; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Vec3i; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.tags.FluidTags; import net.minecraft.util.Mth; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.vehicle.DismountHelper; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.CollisionGetter; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.ExplosionDamageCalculator; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.pathfinder.PathComputationType; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; public class RespawnAnchorBlock extends Block { public static final MapCodec CODEC = simpleCodec(RespawnAnchorBlock::new); public static final int MIN_CHARGES = 0; public static final int MAX_CHARGES = 4; public static final IntegerProperty CHARGE = BlockStateProperties.RESPAWN_ANCHOR_CHARGES; private static final ImmutableList RESPAWN_HORIZONTAL_OFFSETS = ImmutableList.of( new Vec3i(0, 0, -1), new Vec3i(-1, 0, 0), new Vec3i(0, 0, 1), new Vec3i(1, 0, 0), new Vec3i(-1, 0, -1), new Vec3i(1, 0, -1), new Vec3i(-1, 0, 1), new Vec3i(1, 0, 1) ); private static final ImmutableList RESPAWN_OFFSETS = new Builder() .addAll(RESPAWN_HORIZONTAL_OFFSETS) .addAll(RESPAWN_HORIZONTAL_OFFSETS.stream().map(Vec3i::below).iterator()) .addAll(RESPAWN_HORIZONTAL_OFFSETS.stream().map(Vec3i::above).iterator()) .add(new Vec3i(0, 1, 0)) .build(); @Override public MapCodec codec() { return CODEC; } public RespawnAnchorBlock(BlockBehaviour.Properties p_55838_) { super(p_55838_); this.registerDefaultState(this.stateDefinition.any().setValue(CHARGE, 0)); } @Override protected InteractionResult useItemOn( ItemStack p_334655_, BlockState p_333892_, Level p_327960_, BlockPos p_330278_, Player p_331169_, InteractionHand p_328336_, BlockHitResult p_333705_ ) { if (isRespawnFuel(p_334655_) && canBeCharged(p_333892_)) { charge(p_331169_, p_327960_, p_330278_, p_333892_); p_334655_.consume(1, p_331169_); return InteractionResult.SUCCESS; } else { return (InteractionResult)(p_328336_ == InteractionHand.MAIN_HAND && isRespawnFuel(p_331169_.getItemInHand(InteractionHand.OFF_HAND)) && canBeCharged(p_333892_) ? InteractionResult.PASS : InteractionResult.TRY_WITH_EMPTY_HAND); } } @Override protected InteractionResult useWithoutItem(BlockState p_331701_, Level p_333411_, BlockPos p_329077_, Player p_334041_, BlockHitResult p_328905_) { if (p_331701_.getValue(CHARGE) == 0) { return InteractionResult.PASS; } else if (!canSetSpawn(p_333411_)) { if (!p_333411_.isClientSide) { this.explode(p_331701_, p_333411_, p_329077_); } return InteractionResult.SUCCESS; } else { if (p_334041_ instanceof ServerPlayer serverplayer) { ServerPlayer.RespawnConfig serverplayer$respawnconfig = serverplayer.getRespawnConfig(); ServerPlayer.RespawnConfig serverplayer$respawnconfig1 = new ServerPlayer.RespawnConfig(p_333411_.dimension(), p_329077_, 0.0F, false); if (serverplayer$respawnconfig == null || !serverplayer$respawnconfig.isSamePosition(serverplayer$respawnconfig1)) { serverplayer.setRespawnPosition(serverplayer$respawnconfig1, true); p_333411_.playSound( null, p_329077_.getX() + 0.5, p_329077_.getY() + 0.5, p_329077_.getZ() + 0.5, SoundEvents.RESPAWN_ANCHOR_SET_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F ); return InteractionResult.SUCCESS_SERVER; } } return InteractionResult.CONSUME; } } private static boolean isRespawnFuel(ItemStack p_55849_) { return p_55849_.is(Items.GLOWSTONE); } private static boolean canBeCharged(BlockState p_55895_) { return p_55895_.getValue(CHARGE) < 4; } private static boolean isWaterThatWouldFlow(BlockPos p_55888_, Level p_55889_) { FluidState fluidstate = p_55889_.getFluidState(p_55888_); if (!fluidstate.is(FluidTags.WATER)) { return false; } else if (fluidstate.isSource()) { return true; } else { float f = fluidstate.getAmount(); if (f < 2.0F) { return false; } else { FluidState fluidstate1 = p_55889_.getFluidState(p_55888_.below()); return !fluidstate1.is(FluidTags.WATER); } } } private void explode(BlockState p_55891_, Level p_55892_, final BlockPos p_55893_) { p_55892_.removeBlock(p_55893_, false); boolean flag = Direction.Plane.HORIZONTAL.stream().map(p_55893_::relative).anyMatch(p_55854_ -> isWaterThatWouldFlow(p_55854_, p_55892_)); final boolean flag1 = flag || p_55892_.getFluidState(p_55893_.above()).is(FluidTags.WATER); ExplosionDamageCalculator explosiondamagecalculator = new ExplosionDamageCalculator() { @Override public Optional getBlockExplosionResistance(Explosion p_55904_, BlockGetter p_55905_, BlockPos p_55906_, BlockState p_55907_, FluidState p_55908_) { return p_55906_.equals(p_55893_) && flag1 ? Optional.of(Blocks.WATER.getExplosionResistance()) : super.getBlockExplosionResistance(p_55904_, p_55905_, p_55906_, p_55907_, p_55908_); } }; Vec3 vec3 = p_55893_.getCenter(); p_55892_.explode(null, p_55892_.damageSources().badRespawnPointExplosion(vec3), explosiondamagecalculator, vec3, 5.0F, true, Level.ExplosionInteraction.BLOCK); } public static boolean canSetSpawn(Level p_55851_) { return p_55851_.dimensionType().respawnAnchorWorks(); } public static void charge(@Nullable Entity p_270997_, Level p_270172_, BlockPos p_270534_, BlockState p_270661_) { BlockState blockstate = p_270661_.setValue(CHARGE, p_270661_.getValue(CHARGE) + 1); p_270172_.setBlock(p_270534_, blockstate, 3); p_270172_.gameEvent(GameEvent.BLOCK_CHANGE, p_270534_, GameEvent.Context.of(p_270997_, blockstate)); p_270172_.playSound( null, p_270534_.getX() + 0.5, p_270534_.getY() + 0.5, p_270534_.getZ() + 0.5, SoundEvents.RESPAWN_ANCHOR_CHARGE, SoundSource.BLOCKS, 1.0F, 1.0F ); } @Override public void animateTick(BlockState p_221969_, Level p_221970_, BlockPos p_221971_, RandomSource p_221972_) { if (p_221969_.getValue(CHARGE) != 0) { if (p_221972_.nextInt(100) == 0) { p_221970_.playLocalSound(p_221971_, SoundEvents.RESPAWN_ANCHOR_AMBIENT, SoundSource.BLOCKS, 1.0F, 1.0F, false); } double d0 = p_221971_.getX() + 0.5 + (0.5 - p_221972_.nextDouble()); double d1 = p_221971_.getY() + 1.0; double d2 = p_221971_.getZ() + 0.5 + (0.5 - p_221972_.nextDouble()); double d3 = p_221972_.nextFloat() * 0.04; p_221970_.addParticle(ParticleTypes.REVERSE_PORTAL, d0, d1, d2, 0.0, d3, 0.0); } } @Override protected void createBlockStateDefinition(StateDefinition.Builder p_55886_) { p_55886_.add(CHARGE); } @Override protected boolean hasAnalogOutputSignal(BlockState p_55860_) { return true; } public static int getScaledChargeLevel(BlockState p_55862_, int p_55863_) { return Mth.floor((p_55862_.getValue(CHARGE) - 0) / 4.0F * p_55863_); } @Override protected int getAnalogOutputSignal(BlockState p_55870_, Level p_55871_, BlockPos p_55872_) { return getScaledChargeLevel(p_55870_, 15); } public static Optional findStandUpPosition(EntityType p_55840_, CollisionGetter p_55841_, BlockPos p_55842_) { Optional optional = findStandUpPosition(p_55840_, p_55841_, p_55842_, true); return optional.isPresent() ? optional : findStandUpPosition(p_55840_, p_55841_, p_55842_, false); } private static Optional findStandUpPosition(EntityType p_55844_, CollisionGetter p_55845_, BlockPos p_55846_, boolean p_55847_) { BlockPos.MutableBlockPos blockpos$mutableblockpos = new BlockPos.MutableBlockPos(); for (Vec3i vec3i : RESPAWN_OFFSETS) { blockpos$mutableblockpos.set(p_55846_).move(vec3i); Vec3 vec3 = DismountHelper.findSafeDismountLocation(p_55844_, p_55845_, blockpos$mutableblockpos, p_55847_); if (vec3 != null) { return Optional.of(vec3); } } return Optional.empty(); } @Override protected boolean isPathfindable(BlockState p_55865_, PathComputationType p_55868_) { return false; } }