use crate::block::registry::BlockActionResult; use crate::block::{ GetStateForNeighborUpdateArgs, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, UseWithItemArgs, }; use pumpkin_data::BlockStateId; use pumpkin_data::block_properties::{Axis, NoteblockInstrument}; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::{Block, block_properties::NoteBlockLikeProperties}; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; use crate::{ block::{BlockBehaviour, OnSyncedBlockEventArgs}, world::World, }; use super::redstone::block_receives_redstone_power; #[pumpkin_block("minecraft:note_block")] pub struct NoteBlock; impl NoteBlock { pub fn play_note(props: &NoteBlockLikeProperties, world: &World, pos: &BlockPos) { if !is_base_block(props.instrument) || world.get_block_state(&pos.up()).is_air() { let mut event = crate::plugin::api::events::block::note_play::NotePlayEvent::new( *pos, format!("{:?}", props.instrument), props.note, ); if let Some(server) = world.server.upgrade() { server.plugin_manager.fire_blocking(&server, &mut event); } if event.cancelled { return; } world.add_synced_block_event(*pos, 0, 0); } } fn get_note_pitch(note: u16) -> f32 { ((f32::from(note) - 12.0) / 12.0).exp2() } fn get_state_with_instrument( world: &World, pos: &BlockPos, state: BlockStateId, block: &Block, ) -> BlockStateId { let upper_instrument = world.get_block_state(&pos.up()).instrument; let mut note_props = NoteBlockLikeProperties::from_state_id(state); if !is_base_block(upper_instrument) { note_props.instrument = upper_instrument; return note_props.to_state_id(block); } let below_instrument = world.get_block_state(&pos.down()).instrument; let below_instrument = if is_base_block(below_instrument) { below_instrument } else { NoteblockInstrument::Harp }; note_props.instrument = below_instrument; note_props.to_state_id(block) } } impl BlockBehaviour for NoteBlock { fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { let block_state = args.world.get_block_state(args.position); let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id); let powered = block_receives_redstone_power(args.world, args.position); // check if powered state changed if note_props.powered != powered { if powered { Self::play_note(¬e_props, args.world, args.position); } note_props.powered = powered; args.world.set_block_state( args.position, note_props.to_state_id(args.block), BlockFlags::NOTIFY_ALL, ); } } fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { let block_state = args.world.get_block_state(args.position); let mut note_props = NoteBlockLikeProperties::from_state_id(block_state.id); note_props.note = (note_props.note + 1) % 25; args.world.set_block_state( args.position, note_props.to_state_id(args.block), BlockFlags::NOTIFY_ALL, ); Self::play_note(¬e_props, args.world, args.position); args.player.increment_stat( pumpkin_data::statistic::StatisticCategory::Custom, pumpkin_data::statistic::CustomStatistic::TuneNoteblock as i32, 1, ); BlockActionResult::Success } fn use_with_item(&self, _args: UseWithItemArgs<'_>) -> BlockActionResult { // TODO BlockActionResult::PassToDefaultBlockAction } fn on_synced_block_event(&self, args: OnSyncedBlockEventArgs<'_>) -> bool { let block_state = args.world.get_block_state(args.position); let note_props = NoteBlockLikeProperties::from_state_id(block_state.id); let instrument = note_props.instrument; let pitch = if is_base_block(instrument) { // checks if can be pitched Self::get_note_pitch(u16::from(note_props.note)) } else { 1.0 // default pitch }; // check hasCustomSound args.world.play_sound_raw( convert_instrument_to_sound(instrument) as u16, SoundCategory::Records, &args.position.to_f64(), 3.0, pitch, ); true } fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { Self::get_state_with_instrument( args.world, args.position, Block::NOTE_BLOCK.default_state.id, args.block, ) } fn get_state_for_neighbor_update( &self, args: GetStateForNeighborUpdateArgs<'_>, ) -> BlockStateId { if args.direction.to_axis() == Axis::Y { return Self::get_state_with_instrument( args.world, args.position, args.state_id, args.block, ); } args.state_id } } const fn convert_instrument_to_sound(instrument: NoteblockInstrument) -> Sound { match instrument { NoteblockInstrument::Harp => Sound::BlockNoteBlockHarp, NoteblockInstrument::Basedrum => Sound::BlockNoteBlockBasedrum, NoteblockInstrument::Snare => Sound::BlockNoteBlockSnare, NoteblockInstrument::Hat => Sound::BlockNoteBlockHat, NoteblockInstrument::Bass => Sound::BlockNoteBlockBass, NoteblockInstrument::Flute => Sound::BlockNoteBlockFlute, NoteblockInstrument::Bell => Sound::BlockNoteBlockBell, NoteblockInstrument::Guitar => Sound::BlockNoteBlockGuitar, NoteblockInstrument::Chime => Sound::BlockNoteBlockChime, NoteblockInstrument::Xylophone => Sound::BlockNoteBlockXylophone, NoteblockInstrument::IronXylophone => Sound::BlockNoteBlockIronXylophone, NoteblockInstrument::CowBell => Sound::BlockNoteBlockCowBell, NoteblockInstrument::Didgeridoo => Sound::BlockNoteBlockDidgeridoo, NoteblockInstrument::Bit => Sound::BlockNoteBlockBit, NoteblockInstrument::Banjo => Sound::BlockNoteBlockBanjo, NoteblockInstrument::Pling => Sound::BlockNoteBlockPling, NoteblockInstrument::Zombie => Sound::BlockNoteBlockImitateZombie, NoteblockInstrument::Skeleton => Sound::BlockNoteBlockImitateSkeleton, NoteblockInstrument::Creeper => Sound::BlockNoteBlockImitateCreeper, NoteblockInstrument::Dragon => Sound::BlockNoteBlockImitateEnderDragon, NoteblockInstrument::WitherSkeleton => Sound::BlockNoteBlockImitateWitherSkeleton, NoteblockInstrument::Piglin => Sound::BlockNoteBlockImitatePiglin, NoteblockInstrument::CustomHead => Sound::UiButtonClick, NoteblockInstrument::Trumpet => Sound::BlockNoteBlockTrumpet, NoteblockInstrument::TrumpetExposed => Sound::BlockNoteBlockTrumpetExposed, NoteblockInstrument::TrumpetOxidized => Sound::BlockNoteBlockTrumpetOxidized, NoteblockInstrument::TrumpetWeathered => Sound::BlockNoteBlockTrumpetWeathered, } } const fn is_base_block(instrument: NoteblockInstrument) -> bool { matches!( instrument, NoteblockInstrument::Harp | NoteblockInstrument::Basedrum | NoteblockInstrument::Snare | NoteblockInstrument::Hat | NoteblockInstrument::Bass | NoteblockInstrument::Flute | NoteblockInstrument::Bell | NoteblockInstrument::Guitar | NoteblockInstrument::Chime | NoteblockInstrument::Xylophone | NoteblockInstrument::IronXylophone | NoteblockInstrument::CowBell | NoteblockInstrument::Didgeridoo | NoteblockInstrument::Bit | NoteblockInstrument::Banjo ) }