package net.minecraft.world.level.block; import com.mojang.serialization.MapCodec; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Holder; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.resources.ResourceLocation; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundSource; import net.minecraft.stats.Stats; import net.minecraft.tags.ItemTags; 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.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; 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.SkullBlockEntity; 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.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.NoteBlockInstrument; import net.minecraft.world.level.gameevent.GameEvent; import net.minecraft.world.level.redstone.Orientation; import net.minecraft.world.phys.BlockHitResult; import org.jetbrains.annotations.Nullable; public class NoteBlock extends Block { public static final MapCodec CODEC = simpleCodec(NoteBlock::new); public static final EnumProperty INSTRUMENT = BlockStateProperties.NOTEBLOCK_INSTRUMENT; public static final BooleanProperty POWERED = BlockStateProperties.POWERED; public static final IntegerProperty NOTE = BlockStateProperties.NOTE; public static final int NOTE_VOLUME = 3; @Override public MapCodec codec() { return CODEC; } public NoteBlock(BlockBehaviour.Properties properties) { super(properties); this.registerDefaultState(this.stateDefinition.any().setValue(INSTRUMENT, NoteBlockInstrument.HARP).setValue(NOTE, 0).setValue(POWERED, false)); } private BlockState setInstrument(LevelReader level, BlockPos pos, BlockState state) { NoteBlockInstrument noteBlockInstrument = level.getBlockState(pos.above()).instrument(); if (noteBlockInstrument.worksAboveNoteBlock()) { return state.setValue(INSTRUMENT, noteBlockInstrument); } else { NoteBlockInstrument noteBlockInstrument2 = level.getBlockState(pos.below()).instrument(); NoteBlockInstrument noteBlockInstrument3 = noteBlockInstrument2.worksAboveNoteBlock() ? NoteBlockInstrument.HARP : noteBlockInstrument2; return state.setValue(INSTRUMENT, noteBlockInstrument3); } } @Override public BlockState getStateForPlacement(BlockPlaceContext context) { return this.setInstrument(context.getLevel(), context.getClickedPos(), this.defaultBlockState()); } @Override protected BlockState updateShape( BlockState state, LevelReader level, ScheduledTickAccess scheduledTickAccess, BlockPos pos, Direction direction, BlockPos neighborPos, BlockState neighborState, RandomSource random ) { boolean bl = direction.getAxis() == Direction.Axis.Y; return bl ? this.setInstrument(level, pos, state) : super.updateShape(state, level, scheduledTickAccess, pos, direction, neighborPos, neighborState, random); } @Override protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, @Nullable Orientation orientation, boolean movedByPiston) { boolean bl = level.hasNeighborSignal(pos); if (bl != (Boolean)state.getValue(POWERED)) { if (bl) { this.playNote(null, state, level, pos); } level.setBlock(pos, state.setValue(POWERED, bl), 3); } } private void playNote(@Nullable Entity entity, BlockState state, Level level, BlockPos pos) { if (((NoteBlockInstrument)state.getValue(INSTRUMENT)).worksAboveNoteBlock() || level.getBlockState(pos.above()).isAir()) { level.blockEvent(pos, this, 0, 0); level.gameEvent(entity, GameEvent.NOTE_BLOCK_PLAY, pos); } } @Override protected InteractionResult useItemOn( ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult ) { return (InteractionResult)(stack.is(ItemTags.NOTE_BLOCK_TOP_INSTRUMENTS) && hitResult.getDirection() == Direction.UP ? InteractionResult.PASS : super.useItemOn(stack, state, level, pos, player, hand, hitResult)); } @Override protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos, Player player, BlockHitResult hitResult) { if (!level.isClientSide) { state = state.cycle(NOTE); level.setBlock(pos, state, 3); this.playNote(player, state, level, pos); player.awardStat(Stats.TUNE_NOTEBLOCK); } return InteractionResult.SUCCESS; } @Override protected void attack(BlockState state, Level level, BlockPos pos, Player player) { if (!level.isClientSide) { this.playNote(player, state, level, pos); player.awardStat(Stats.PLAY_NOTEBLOCK); } } public static float getPitchFromNote(int note) { return (float)Math.pow(2.0, (note - 12) / 12.0); } @Override protected boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) { NoteBlockInstrument noteBlockInstrument = state.getValue(INSTRUMENT); float f; if (noteBlockInstrument.isTunable()) { int i = (Integer)state.getValue(NOTE); f = getPitchFromNote(i); level.addParticle(ParticleTypes.NOTE, pos.getX() + 0.5, pos.getY() + 1.2, pos.getZ() + 0.5, i / 24.0, 0.0, 0.0); } else { f = 1.0F; } Holder holder; if (noteBlockInstrument.hasCustomSound()) { ResourceLocation resourceLocation = this.getCustomSoundId(level, pos); if (resourceLocation == null) { return false; } holder = Holder.direct(SoundEvent.createVariableRangeEvent(resourceLocation)); } else { holder = noteBlockInstrument.getSoundEvent(); } level.playSeededSound(null, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, holder, SoundSource.RECORDS, 3.0F, f, level.random.nextLong()); return true; } @Nullable private ResourceLocation getCustomSoundId(Level level, BlockPos pos) { return level.getBlockEntity(pos.above()) instanceof SkullBlockEntity skullBlockEntity ? skullBlockEntity.getNoteBlockSound() : null; } @Override protected void createBlockStateDefinition(Builder builder) { builder.add(INSTRUMENT, POWERED, NOTE); } }