223 lines
8.6 KiB
Java
223 lines
8.6 KiB
Java
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 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.ItemInteractionResult;
|
|
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;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class RespawnAnchorBlock extends Block {
|
|
public static final MapCodec<RespawnAnchorBlock> 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<Vec3i> 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<Vec3i> RESPAWN_OFFSETS = new Builder<Vec3i>()
|
|
.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<RespawnAnchorBlock> codec() {
|
|
return CODEC;
|
|
}
|
|
|
|
public RespawnAnchorBlock(BlockBehaviour.Properties properties) {
|
|
super(properties);
|
|
this.registerDefaultState(this.stateDefinition.any().setValue(CHARGE, 0));
|
|
}
|
|
|
|
@Override
|
|
protected ItemInteractionResult useItemOn(
|
|
ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult
|
|
) {
|
|
if (isRespawnFuel(stack) && canBeCharged(state)) {
|
|
charge(player, level, pos, state);
|
|
stack.consume(1, player);
|
|
return ItemInteractionResult.sidedSuccess(level.isClientSide);
|
|
} else {
|
|
return hand == InteractionHand.MAIN_HAND && isRespawnFuel(player.getItemInHand(InteractionHand.OFF_HAND)) && canBeCharged(state)
|
|
? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION
|
|
: ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) {
|
|
if ((Integer)state.getValue(CHARGE) == 0) {
|
|
return InteractionResult.PASS;
|
|
} else if (!canSetSpawn(level)) {
|
|
if (!level.isClientSide) {
|
|
this.explode(state, level, pos);
|
|
}
|
|
|
|
return InteractionResult.sidedSuccess(level.isClientSide);
|
|
} else {
|
|
if (!level.isClientSide) {
|
|
ServerPlayer serverPlayer = (ServerPlayer)player;
|
|
if (serverPlayer.getRespawnDimension() != level.dimension() || !pos.equals(serverPlayer.getRespawnPosition())) {
|
|
serverPlayer.setRespawnPosition(level.dimension(), pos, 0.0F, false, true);
|
|
level.playSound(null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, SoundEvents.RESPAWN_ANCHOR_SET_SPAWN, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
return InteractionResult.SUCCESS;
|
|
}
|
|
}
|
|
|
|
return InteractionResult.CONSUME;
|
|
}
|
|
}
|
|
|
|
private static boolean isRespawnFuel(ItemStack stack) {
|
|
return stack.is(Items.GLOWSTONE);
|
|
}
|
|
|
|
private static boolean canBeCharged(BlockState state) {
|
|
return (Integer)state.getValue(CHARGE) < 4;
|
|
}
|
|
|
|
private static boolean isWaterThatWouldFlow(BlockPos pos, Level level) {
|
|
FluidState fluidState = level.getFluidState(pos);
|
|
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 fluidState2 = level.getFluidState(pos.below());
|
|
return !fluidState2.is(FluidTags.WATER);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void explode(BlockState state, Level level, BlockPos pos2) {
|
|
level.removeBlock(pos2, false);
|
|
boolean bl = Direction.Plane.HORIZONTAL.stream().map(pos2::relative).anyMatch(blockPos -> isWaterThatWouldFlow(blockPos, level));
|
|
final boolean bl2 = bl || level.getFluidState(pos2.above()).is(FluidTags.WATER);
|
|
ExplosionDamageCalculator explosionDamageCalculator = new ExplosionDamageCalculator() {
|
|
@Override
|
|
public Optional<Float> getBlockExplosionResistance(Explosion explosion, BlockGetter reader, BlockPos pos, BlockState statex, FluidState fluid) {
|
|
return pos.equals(pos2) && bl2
|
|
? Optional.of(Blocks.WATER.getExplosionResistance())
|
|
: super.getBlockExplosionResistance(explosion, reader, pos, statex, fluid);
|
|
}
|
|
};
|
|
Vec3 vec3 = pos2.getCenter();
|
|
level.explode(null, level.damageSources().badRespawnPointExplosion(vec3), explosionDamageCalculator, vec3, 5.0F, true, Level.ExplosionInteraction.BLOCK);
|
|
}
|
|
|
|
public static boolean canSetSpawn(Level level) {
|
|
return level.dimensionType().respawnAnchorWorks();
|
|
}
|
|
|
|
public static void charge(@Nullable Entity entity, Level level, BlockPos pos, BlockState state) {
|
|
BlockState blockState = state.setValue(CHARGE, (Integer)state.getValue(CHARGE) + 1);
|
|
level.setBlock(pos, blockState, 3);
|
|
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(entity, blockState));
|
|
level.playSound(null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, SoundEvents.RESPAWN_ANCHOR_CHARGE, SoundSource.BLOCKS, 1.0F, 1.0F);
|
|
}
|
|
|
|
@Override
|
|
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
|
|
if ((Integer)state.getValue(CHARGE) != 0) {
|
|
if (random.nextInt(100) == 0) {
|
|
level.playLocalSound(pos, SoundEvents.RESPAWN_ANCHOR_AMBIENT, SoundSource.BLOCKS, 1.0F, 1.0F, false);
|
|
}
|
|
|
|
double d = pos.getX() + 0.5 + (0.5 - random.nextDouble());
|
|
double e = pos.getY() + 1.0;
|
|
double f = pos.getZ() + 0.5 + (0.5 - random.nextDouble());
|
|
double g = random.nextFloat() * 0.04;
|
|
level.addParticle(ParticleTypes.REVERSE_PORTAL, d, e, f, 0.0, g, 0.0);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
|
|
builder.add(CHARGE);
|
|
}
|
|
|
|
@Override
|
|
protected boolean hasAnalogOutputSignal(BlockState state) {
|
|
return true;
|
|
}
|
|
|
|
public static int getScaledChargeLevel(BlockState state, int scale) {
|
|
return Mth.floor(((Integer)state.getValue(CHARGE) - 0) / 4.0F * scale);
|
|
}
|
|
|
|
@Override
|
|
protected int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos) {
|
|
return getScaledChargeLevel(state, 15);
|
|
}
|
|
|
|
public static Optional<Vec3> findStandUpPosition(EntityType<?> entityType, CollisionGetter level, BlockPos pos) {
|
|
Optional<Vec3> optional = findStandUpPosition(entityType, level, pos, true);
|
|
return optional.isPresent() ? optional : findStandUpPosition(entityType, level, pos, false);
|
|
}
|
|
|
|
private static Optional<Vec3> findStandUpPosition(EntityType<?> entityType, CollisionGetter level, BlockPos pos, boolean simulate) {
|
|
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
|
|
|
for (Vec3i vec3i : RESPAWN_OFFSETS) {
|
|
mutableBlockPos.set(pos).move(vec3i);
|
|
Vec3 vec3 = DismountHelper.findSafeDismountLocation(entityType, level, mutableBlockPos, simulate);
|
|
if (vec3 != null) {
|
|
return Optional.of(vec3);
|
|
}
|
|
}
|
|
|
|
return Optional.empty();
|
|
}
|
|
|
|
@Override
|
|
protected boolean isPathfindable(BlockState state, PathComputationType pathComputationType) {
|
|
return false;
|
|
}
|
|
}
|