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.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.ItemInteractionResult; 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.LevelAccessor; 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; 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.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(LevelAccessor 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, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) { boolean bl = direction.getAxis() == Direction.Axis.Y; return bl ? this.setInstrument(level, pos, state) : super.updateShape(state, direction, neighborState, level, pos, neighborPos); } @Override protected void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, 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 ItemInteractionResult useItemOn( ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult ) { return stack.is(ItemTags.NOTE_BLOCK_TOP_INSTRUMENTS) && hitResult.getDirection() == Direction.UP ? ItemInteractionResult.SKIP_DEFAULT_BLOCK_INTERACTION : 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) { return InteractionResult.SUCCESS; } else { state = state.cycle(NOTE); level.setBlock(pos, state, 3); this.playNote(player, state, level, pos); player.awardStat(Stats.TUNE_NOTEBLOCK); return InteractionResult.CONSUME; } } @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(StateDefinition.Builder builder) { builder.add(INSTRUMENT, POWERED, NOTE); } }