290 lines
13 KiB
Java
290 lines
13 KiB
Java
|
package net.minecraft.world.level.block;
|
||
|
|
||
|
import com.google.common.annotations.VisibleForTesting;
|
||
|
import com.mojang.serialization.MapCodec;
|
||
|
import javax.annotation.Nullable;
|
||
|
import net.minecraft.Util;
|
||
|
import net.minecraft.core.BlockPos;
|
||
|
import net.minecraft.core.Direction;
|
||
|
import net.minecraft.core.particles.DustColorTransitionOptions;
|
||
|
import net.minecraft.server.level.ServerLevel;
|
||
|
import net.minecraft.sounds.SoundEvents;
|
||
|
import net.minecraft.sounds.SoundSource;
|
||
|
import net.minecraft.tags.BlockTags;
|
||
|
import net.minecraft.util.RandomSource;
|
||
|
import net.minecraft.util.valueproviders.ConstantInt;
|
||
|
import net.minecraft.world.entity.Entity;
|
||
|
import net.minecraft.world.entity.EntityType;
|
||
|
import net.minecraft.world.item.ItemStack;
|
||
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||
|
import net.minecraft.world.level.BlockGetter;
|
||
|
import net.minecraft.world.level.Level;
|
||
|
import net.minecraft.world.level.LevelReader;
|
||
|
import net.minecraft.world.level.ScheduledTickAccess;
|
||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||
|
import net.minecraft.world.level.block.entity.BlockEntityTicker;
|
||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||
|
import net.minecraft.world.level.block.entity.SculkSensorBlockEntity;
|
||
|
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.BooleanProperty;
|
||
|
import net.minecraft.world.level.block.state.properties.EnumProperty;
|
||
|
import net.minecraft.world.level.block.state.properties.IntegerProperty;
|
||
|
import net.minecraft.world.level.block.state.properties.SculkSensorPhase;
|
||
|
import net.minecraft.world.level.gameevent.GameEvent;
|
||
|
import net.minecraft.world.level.gameevent.vibrations.VibrationSystem;
|
||
|
import net.minecraft.world.level.material.FluidState;
|
||
|
import net.minecraft.world.level.material.Fluids;
|
||
|
import net.minecraft.world.level.pathfinder.PathComputationType;
|
||
|
import net.minecraft.world.phys.shapes.CollisionContext;
|
||
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||
|
|
||
|
public class SculkSensorBlock extends BaseEntityBlock implements SimpleWaterloggedBlock {
|
||
|
public static final MapCodec<SculkSensorBlock> CODEC = simpleCodec(SculkSensorBlock::new);
|
||
|
public static final int ACTIVE_TICKS = 30;
|
||
|
public static final int COOLDOWN_TICKS = 10;
|
||
|
public static final EnumProperty<SculkSensorPhase> PHASE = BlockStateProperties.SCULK_SENSOR_PHASE;
|
||
|
public static final IntegerProperty POWER = BlockStateProperties.POWER;
|
||
|
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
|
||
|
private static final VoxelShape SHAPE = Block.column(16.0, 0.0, 8.0);
|
||
|
private static final float[] RESONANCE_PITCH_BEND = Util.make(new float[16], p_277301_ -> {
|
||
|
int[] aint = new int[]{0, 0, 2, 4, 6, 7, 9, 10, 12, 14, 15, 18, 19, 21, 22, 24};
|
||
|
|
||
|
for (int i = 0; i < 16; i++) {
|
||
|
p_277301_[i] = NoteBlock.getPitchFromNote(aint[i]);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
@Override
|
||
|
public MapCodec<? extends SculkSensorBlock> codec() {
|
||
|
return CODEC;
|
||
|
}
|
||
|
|
||
|
public SculkSensorBlock(BlockBehaviour.Properties p_277588_) {
|
||
|
super(p_277588_);
|
||
|
this.registerDefaultState(this.stateDefinition.any().setValue(PHASE, SculkSensorPhase.INACTIVE).setValue(POWER, 0).setValue(WATERLOGGED, false));
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public BlockState getStateForPlacement(BlockPlaceContext p_154396_) {
|
||
|
BlockPos blockpos = p_154396_.getClickedPos();
|
||
|
FluidState fluidstate = p_154396_.getLevel().getFluidState(blockpos);
|
||
|
return this.defaultBlockState().setValue(WATERLOGGED, fluidstate.getType() == Fluids.WATER);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected FluidState getFluidState(BlockState p_154479_) {
|
||
|
return p_154479_.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(p_154479_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void tick(BlockState p_222137_, ServerLevel p_222138_, BlockPos p_222139_, RandomSource p_222140_) {
|
||
|
if (getPhase(p_222137_) != SculkSensorPhase.ACTIVE) {
|
||
|
if (getPhase(p_222137_) == SculkSensorPhase.COOLDOWN) {
|
||
|
p_222138_.setBlock(p_222139_, p_222137_.setValue(PHASE, SculkSensorPhase.INACTIVE), 3);
|
||
|
if (!p_222137_.getValue(WATERLOGGED)) {
|
||
|
p_222138_.playSound(null, p_222139_, SoundEvents.SCULK_CLICKING_STOP, SoundSource.BLOCKS, 1.0F, p_222138_.random.nextFloat() * 0.2F + 0.8F);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
deactivate(p_222138_, p_222139_, p_222137_);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void stepOn(Level p_222132_, BlockPos p_222133_, BlockState p_222134_, Entity p_222135_) {
|
||
|
if (!p_222132_.isClientSide()
|
||
|
&& canActivate(p_222134_)
|
||
|
&& p_222135_.getType() != EntityType.WARDEN
|
||
|
&& p_222132_.getBlockEntity(p_222133_) instanceof SculkSensorBlockEntity sculksensorblockentity
|
||
|
&& p_222132_ instanceof ServerLevel serverlevel
|
||
|
&& sculksensorblockentity.getVibrationUser().canReceiveVibration(serverlevel, p_222133_, GameEvent.STEP, GameEvent.Context.of(p_222134_))) {
|
||
|
sculksensorblockentity.getListener().forceScheduleVibration(serverlevel, GameEvent.STEP, GameEvent.Context.of(p_222135_), p_222135_.position());
|
||
|
}
|
||
|
|
||
|
super.stepOn(p_222132_, p_222133_, p_222134_, p_222135_);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void onPlace(BlockState p_154471_, Level p_154472_, BlockPos p_154473_, BlockState p_154474_, boolean p_154475_) {
|
||
|
if (!p_154472_.isClientSide() && !p_154471_.is(p_154474_.getBlock())) {
|
||
|
if (p_154471_.getValue(POWER) > 0 && !p_154472_.getBlockTicks().hasScheduledTick(p_154473_, this)) {
|
||
|
p_154472_.setBlock(p_154473_, p_154471_.setValue(POWER, 0), 18);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void affectNeighborsAfterRemoval(BlockState p_393130_, ServerLevel p_396581_, BlockPos p_396205_, boolean p_393330_) {
|
||
|
if (getPhase(p_393130_) == SculkSensorPhase.ACTIVE) {
|
||
|
updateNeighbours(p_396581_, p_396205_, p_393130_);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected BlockState updateShape(
|
||
|
BlockState p_154457_,
|
||
|
LevelReader p_368197_,
|
||
|
ScheduledTickAccess p_370168_,
|
||
|
BlockPos p_154461_,
|
||
|
Direction p_154458_,
|
||
|
BlockPos p_154462_,
|
||
|
BlockState p_154459_,
|
||
|
RandomSource p_369381_
|
||
|
) {
|
||
|
if (p_154457_.getValue(WATERLOGGED)) {
|
||
|
p_370168_.scheduleTick(p_154461_, Fluids.WATER, Fluids.WATER.getTickDelay(p_368197_));
|
||
|
}
|
||
|
|
||
|
return super.updateShape(p_154457_, p_368197_, p_370168_, p_154461_, p_154458_, p_154462_, p_154459_, p_369381_);
|
||
|
}
|
||
|
|
||
|
private static void updateNeighbours(Level p_278067_, BlockPos p_277440_, BlockState p_277354_) {
|
||
|
Block block = p_277354_.getBlock();
|
||
|
p_278067_.updateNeighborsAt(p_277440_, block);
|
||
|
p_278067_.updateNeighborsAt(p_277440_.below(), block);
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public BlockEntity newBlockEntity(BlockPos p_154466_, BlockState p_154467_) {
|
||
|
return new SculkSensorBlockEntity(p_154466_, p_154467_);
|
||
|
}
|
||
|
|
||
|
@Nullable
|
||
|
@Override
|
||
|
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level p_154401_, BlockState p_154402_, BlockEntityType<T> p_154403_) {
|
||
|
return !p_154401_.isClientSide
|
||
|
? createTickerHelper(
|
||
|
p_154403_,
|
||
|
BlockEntityType.SCULK_SENSOR,
|
||
|
(p_281130_, p_281131_, p_281132_, p_281133_) -> VibrationSystem.Ticker.tick(p_281130_, p_281133_.getVibrationData(), p_281133_.getVibrationUser())
|
||
|
)
|
||
|
: null;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected VoxelShape getShape(BlockState p_154432_, BlockGetter p_154433_, BlockPos p_154434_, CollisionContext p_154435_) {
|
||
|
return SHAPE;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected boolean isSignalSource(BlockState p_154484_) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected int getSignal(BlockState p_154437_, BlockGetter p_154438_, BlockPos p_154439_, Direction p_154440_) {
|
||
|
return p_154437_.getValue(POWER);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public int getDirectSignal(BlockState p_279407_, BlockGetter p_279217_, BlockPos p_279190_, Direction p_279273_) {
|
||
|
return p_279273_ == Direction.UP ? p_279407_.getSignal(p_279217_, p_279190_, p_279273_) : 0;
|
||
|
}
|
||
|
|
||
|
public static SculkSensorPhase getPhase(BlockState p_154488_) {
|
||
|
return p_154488_.getValue(PHASE);
|
||
|
}
|
||
|
|
||
|
public static boolean canActivate(BlockState p_154490_) {
|
||
|
return getPhase(p_154490_) == SculkSensorPhase.INACTIVE;
|
||
|
}
|
||
|
|
||
|
public static void deactivate(Level p_154408_, BlockPos p_154409_, BlockState p_154410_) {
|
||
|
p_154408_.setBlock(p_154409_, p_154410_.setValue(PHASE, SculkSensorPhase.COOLDOWN).setValue(POWER, 0), 3);
|
||
|
p_154408_.scheduleTick(p_154409_, p_154410_.getBlock(), 10);
|
||
|
updateNeighbours(p_154408_, p_154409_, p_154410_);
|
||
|
}
|
||
|
|
||
|
@VisibleForTesting
|
||
|
public int getActiveTicks() {
|
||
|
return 30;
|
||
|
}
|
||
|
|
||
|
public void activate(@Nullable Entity p_277529_, Level p_277340_, BlockPos p_277386_, BlockState p_277799_, int p_277993_, int p_278003_) {
|
||
|
p_277340_.setBlock(p_277386_, p_277799_.setValue(PHASE, SculkSensorPhase.ACTIVE).setValue(POWER, p_277993_), 3);
|
||
|
p_277340_.scheduleTick(p_277386_, p_277799_.getBlock(), this.getActiveTicks());
|
||
|
updateNeighbours(p_277340_, p_277386_, p_277799_);
|
||
|
tryResonateVibration(p_277529_, p_277340_, p_277386_, p_278003_);
|
||
|
p_277340_.gameEvent(p_277529_, GameEvent.SCULK_SENSOR_TENDRILS_CLICKING, p_277386_);
|
||
|
if (!p_277799_.getValue(WATERLOGGED)) {
|
||
|
p_277340_.playSound(
|
||
|
null,
|
||
|
p_277386_.getX() + 0.5,
|
||
|
p_277386_.getY() + 0.5,
|
||
|
p_277386_.getZ() + 0.5,
|
||
|
SoundEvents.SCULK_CLICKING,
|
||
|
SoundSource.BLOCKS,
|
||
|
1.0F,
|
||
|
p_277340_.random.nextFloat() * 0.2F + 0.8F
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void tryResonateVibration(@Nullable Entity p_279315_, Level p_277804_, BlockPos p_277458_, int p_277347_) {
|
||
|
for (Direction direction : Direction.values()) {
|
||
|
BlockPos blockpos = p_277458_.relative(direction);
|
||
|
BlockState blockstate = p_277804_.getBlockState(blockpos);
|
||
|
if (blockstate.is(BlockTags.VIBRATION_RESONATORS)) {
|
||
|
p_277804_.gameEvent(VibrationSystem.getResonanceEventByFrequency(p_277347_), blockpos, GameEvent.Context.of(p_279315_, blockstate));
|
||
|
float f = RESONANCE_PITCH_BEND[p_277347_];
|
||
|
p_277804_.playSound(null, blockpos, SoundEvents.AMETHYST_BLOCK_RESONATE, SoundSource.BLOCKS, 1.0F, f);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
public void animateTick(BlockState p_222148_, Level p_222149_, BlockPos p_222150_, RandomSource p_222151_) {
|
||
|
if (getPhase(p_222148_) == SculkSensorPhase.ACTIVE) {
|
||
|
Direction direction = Direction.getRandom(p_222151_);
|
||
|
if (direction != Direction.UP && direction != Direction.DOWN) {
|
||
|
double d0 = p_222150_.getX() + 0.5 + (direction.getStepX() == 0 ? 0.5 - p_222151_.nextDouble() : direction.getStepX() * 0.6);
|
||
|
double d1 = p_222150_.getY() + 0.25;
|
||
|
double d2 = p_222150_.getZ() + 0.5 + (direction.getStepZ() == 0 ? 0.5 - p_222151_.nextDouble() : direction.getStepZ() * 0.6);
|
||
|
double d3 = p_222151_.nextFloat() * 0.04;
|
||
|
p_222149_.addParticle(DustColorTransitionOptions.SCULK_TO_REDSTONE, d0, d1, d2, 0.0, d3, 0.0);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> p_154464_) {
|
||
|
p_154464_.add(PHASE, POWER, WATERLOGGED);
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected boolean hasAnalogOutputSignal(BlockState p_154481_) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected int getAnalogOutputSignal(BlockState p_154442_, Level p_154443_, BlockPos p_154444_) {
|
||
|
if (p_154443_.getBlockEntity(p_154444_) instanceof SculkSensorBlockEntity sculksensorblockentity) {
|
||
|
return getPhase(p_154442_) == SculkSensorPhase.ACTIVE ? sculksensorblockentity.getLastVibrationFrequency() : 0;
|
||
|
} else {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected boolean isPathfindable(BlockState p_154427_, PathComputationType p_154430_) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected boolean useShapeForLightOcclusion(BlockState p_154486_) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
@Override
|
||
|
protected void spawnAfterBreak(BlockState p_222142_, ServerLevel p_222143_, BlockPos p_222144_, ItemStack p_222145_, boolean p_222146_) {
|
||
|
super.spawnAfterBreak(p_222142_, p_222143_, p_222144_, p_222145_, p_222146_);
|
||
|
if (p_222146_) {
|
||
|
this.tryDropExperience(p_222143_, p_222144_, p_222145_, ConstantInt.of(5));
|
||
|
}
|
||
|
}
|
||
|
}
|