package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import java.util.List; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.RandomSource; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.decoration.ItemFrame; import net.minecraft.world.entity.player.Player; 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.ComparatorBlockEntity; import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition.Builder; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.ComparatorMode; import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.ticks.TickPriority; import org.jetbrains.annotations.Nullable; public class ComparatorBlock extends DiodeBlock implements EntityBlock { public static final MapCodec CODEC = simpleCodec(ComparatorBlock::new); public static final EnumProperty MODE = BlockStateProperties.MODE_COMPARATOR; @Override public MapCodec codec() { return CODEC; } public ComparatorBlock(BlockBehaviour.Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(FACING, Direction.NORTH).setValue(POWERED, false).setValue(MODE, ComparatorMode.COMPARE)); } @Override protected int getDelay(BlockState state) { return 2; } @Override public BlockState updateShape( BlockState state, LevelReader level, ScheduledTickAccess scheduledTickAccess, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random ) { return direction == Direction.DOWN && !this.canSurviveOn(level, neighborPos, neighborState) ? Blocks.AIR.defaultBlockState() : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); } @Override protected int getOutputSignal(BlockGetter level, BlockPos pos, BlockState state) { BlockEntity blockEntity = level.getBlockEntity(pos); return blockEntity instanceof ComparatorBlockEntity ? ((ComparatorBlockEntity)blockEntity).getOutputSignal() : 0; } private int calculateOutputSignal(Level level, BlockPos pos, BlockState state) { int i = this.getInputSignal(level, pos, state); if (i == 0) { return 0; } else { int j = this.getAlternateSignal(level, pos, state); if (j > i) { return 0; } else { return state.getValue(MODE) == ComparatorMode.SUBTRACT ? i - j : i; } } } @Override protected boolean shouldTurnOn(Level level, BlockPos pos, BlockState state) { int i = this.getInputSignal(level, pos, state); if (i == 0) { return false; } else { int j = this.getAlternateSignal(level, pos, state); return i > j ? true : i == j && state.getValue(MODE) == ComparatorMode.COMPARE; } } @Override protected int getInputSignal(Level level, BlockPos pos, BlockState state) { int i = super.getInputSignal(level, pos, state); Direction direction = state.getValue(FACING); BlockPos blockPos = pos.relative(direction); BlockState blockState = level.getBlockState(blockPos); if (blockState.hasAnalogOutputSignal()) { i = blockState.getAnalogOutputSignal(level, blockPos); } else if (i < 15 && blockState.isRedstoneConductor(level, blockPos)) { blockPos = blockPos.relative(direction); blockState = level.getBlockState(blockPos); ItemFrame itemFrame = this.getItemFrame(level, direction, blockPos); int j = Math.max( itemFrame == null ? Integer.MIN_VALUE : itemFrame.getAnalogOutput(), blockState.hasAnalogOutputSignal() ? blockState.getAnalogOutputSignal(level, blockPos) : Integer.MIN_VALUE ); if (j != Integer.MIN_VALUE) { i = j; } } return i; } @Nullable private ItemFrame getItemFrame(Level level, Direction facing, BlockPos pos) { List list = level.getEntitiesOfClass( ItemFrame.class, new AABB(pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1), itemFrame -> itemFrame != null && itemFrame.getDirection() == facing ); return list.size() == 1 ? (ItemFrame)list.get(0) : null; } @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { if (!player.getAbilities().mayBuild) { return InteractionResult.PASS; } else { state = state.cycle(MODE); float f = state.getValue(MODE) == ComparatorMode.SUBTRACT ? 0.55F : 0.5F; level.playSound(player, pos, SoundEvents.COMPARATOR_CLICK, SoundSource.BLOCKS, 0.3F, f); level.setBlock(pos, state, 2); this.refreshOutputState(level, pos, state); return InteractionResult.SUCCESS; } } @Override protected void checkTickOnNeighbor(Level level, BlockPos pos, BlockState state) { if (!level.getBlockTicks().willTickThisTick(pos, this)) { int i = this.calculateOutputSignal(level, pos, state); BlockEntity blockEntity = level.getBlockEntity(pos); int j = blockEntity instanceof ComparatorBlockEntity ? ((ComparatorBlockEntity)blockEntity).getOutputSignal() : 0; if (i != j || (Boolean)state.getValue(POWERED) != this.shouldTurnOn(level, pos, state)) { TickPriority tickPriority = this.shouldPrioritize(level, pos, state) ? TickPriority.HIGH : TickPriority.NORMAL; level.scheduleTick(pos, this, 2, tickPriority); } } } private void refreshOutputState(Level level, BlockPos pos, BlockState state) { int i = this.calculateOutputSignal(level, pos, state); BlockEntity blockEntity = level.getBlockEntity(pos); int j = 0; if (blockEntity instanceof ComparatorBlockEntity comparatorBlockEntity) { j = comparatorBlockEntity.getOutputSignal(); comparatorBlockEntity.setOutputSignal(i); } if (j != i || state.getValue(MODE) == ComparatorMode.COMPARE) { boolean bl = this.shouldTurnOn(level, pos, state); boolean bl2 = (Boolean)state.getValue(POWERED); if (bl2 && !bl) { level.setBlock(pos, state.setValue(POWERED, false), 2); } else if (!bl2 && bl) { level.setBlock(pos, state.setValue(POWERED, true), 2); } this.updateNeighborsInFront(level, pos, state); } } @Override protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { this.refreshOutputState(level, pos, state); } @Override protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) { super.triggerEvent(state, level, pos, id, param); BlockEntity blockEntity = level.getBlockEntity(pos); return blockEntity != null && blockEntity.triggerEvent(id, param); } @Override public BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new ComparatorBlockEntity(pos, state); } @Override protected void createBlockStateDefinition(Builder builder) { builder.add(FACING, MODE, POWERED); } }