diff --git a/crates/blocks/src/blocks/mod.rs b/crates/blocks/src/blocks/mod.rs index 8f313084..43e8618b 100644 --- a/crates/blocks/src/blocks/mod.rs +++ b/crates/blocks/src/blocks/mod.rs @@ -60,6 +60,7 @@ noop_block_transform!( ButtonFace, LeverFace, ComparatorMode, + Instrument, ); impl BlockTransform for BlockDirection { @@ -1191,6 +1192,145 @@ blocks! { }, get_name: "iron_trapdoor", }, + NoteBlock { + props: { + instrument: Instrument, + note: u32, + powered: bool + }, + get_id: { + instrument.get_id() * 50 + + note * 2 + + !powered as u32 + + 281 + }, + from_id_offset: 281, + from_id(id): 281..=1080 => { + instrument: Instrument::from_id((id >> 1) / 25), + note: (id >> 1) % 25, + powered: (id & 1) == 0 + }, + from_names(_name): { + "note_block" => { + instrument: Instrument::Harp, + note: 0, + powered: false + } + }, + get_name: "note_block", + solid: true, + cube: true, + }, + Clay { + props: {}, + get_id: 4016, + from_id(_id): 4016 => {}, + from_names(_name): { + "clay" => {} + }, + get_name: "clay", + solid: true, + cube: true, + }, + GoldBlock { + props: {}, + get_id: 1483, + from_id(_id): 1483 => {}, + from_names(_name): { + "gold_block" => {} + }, + get_name: "gold_block", + solid: true, + cube: true, + }, + PackedIce { + props: {}, + get_id: 8134, + from_id(_id): 8134 => {}, + from_names(_name): { + "packed_ice" => {} + }, + get_name: "packed_ice", + solid: true, + cube: true, + }, + BoneBlock { + props: {}, + get_id: 9507, + from_id(_id): 9506..=9508 => {}, + from_names(_name): { + "bone_block" => {} + }, + get_name: "bone_block", + solid: true, + cube: true, + }, + IronBlock { + props: {}, + get_id: 1484, + from_id(_id): 1484 => {}, + from_names(_name): { + "iron_block" => {} + }, + get_name: "iron_block", + solid: true, + cube: true, + }, + SoulSand { + props: {}, + get_id: 4069, + from_id(_id): 4069 => {}, + from_names(_name): { + "soul_sand" => {} + }, + get_name: "soul_sand", + solid: true, + cube: true, + }, + Pumpkin { + props: {}, + get_id: 4067, + from_id(_id): 4067 => {}, + from_names(_name): { + "pumpkin" => {} + }, + get_name: "pumpkin", + solid: true, + cube: true, + }, + EmeraldBlock { + props: {}, + get_id: 5609, + from_id(_id): 5609 => {}, + from_names(_name): { + "emerald_block" => {} + }, + get_name: "emerald_block", + solid: true, + cube: true, + }, + HayBlock { + props: {}, + get_id: 8114, + from_id(_id): 8113..=8115 => {}, + from_names(_name): { + "hay_block" => {} + }, + get_name: "hay_block", + solid: true, + cube: true, + }, + Sand { + props: {}, + get_id: 66, + from_id(_id): 66 => {}, + from_names(_name): { + "sand" => {} + }, + get_name: "sand", + solid: true, + cube: true, + }, Unknown { props: { id: u32 diff --git a/crates/blocks/src/blocks/props.rs b/crates/blocks/src/blocks/props.rs index f2780178..cb564e7d 100644 --- a/crates/blocks/src/blocks/props.rs +++ b/crates/blocks/src/blocks/props.rs @@ -1,4 +1,4 @@ -use super::{BlockDirection, BlockProperty, BlockTransform, FlipDirection}; +use super::{Block, BlockDirection, BlockProperty, BlockTransform, FlipDirection}; use std::str::FromStr; #[derive(Copy, Clone, Debug, PartialEq, Eq, BlockProperty, BlockTransform)] @@ -390,3 +390,156 @@ impl FromStr for TrapdoorHalf { }) } } + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] +pub enum Instrument { + Harp, + Basedrum, + Snare, + Hat, + Bass, + Flute, + Bell, + Guitar, + Chime, + Xylophone, + IronXylophone, + CowBell, + Didgeridoo, + Bit, + Banjo, + Pling, +} + +impl Instrument { + pub fn get_id(self) -> u32 { + self as u32 + } + + pub fn from_id(id: u32) -> Self { + match id { + 0 => Instrument::Harp, + 1 => Instrument::Basedrum, + 2 => Instrument::Snare, + 3 => Instrument::Hat, + 4 => Instrument::Bass, + 5 => Instrument::Flute, + 6 => Instrument::Bell, + 7 => Instrument::Guitar, + 8 => Instrument::Chime, + 9 => Instrument::Xylophone, + 10 => Instrument::IronXylophone, + 11 => Instrument::CowBell, + 12 => Instrument::Didgeridoo, + 13 => Instrument::Bit, + 14 => Instrument::Banjo, + 15 => Instrument::Pling, + _ => unreachable!(), + } + } + + pub fn from_block_below(block: Block) -> Instrument { + match block { + // All stone materials + Block::Stone {} + | Block::CoalBlock {} + | Block::Quartz {} + | Block::Sandstone {} + | Block::Concrete { .. } + | Block::Terracotta {} + | Block::ColoredTerracotta { .. } => Instrument::Basedrum, + // All sand/aggregate materials: ConcretePowder + Block::Sand {} => Instrument::Snare, + // All glass materials: GlassPane + Block::Glass {} | Block::StainedGlass { .. } => Instrument::Hat, + // All wood materials: Log, Plank + Block::Sign { .. } + | Block::NoteBlock { .. } + | Block::Barrel {} + | Block::Composter { .. } => Instrument::Bass, + Block::Clay {} => Instrument::Flute, + Block::GoldBlock {} => Instrument::Bell, + Block::Wool { .. } => Instrument::Guitar, + Block::PackedIce {} => Instrument::Chime, + Block::BoneBlock {} => Instrument::Xylophone, + Block::IronBlock {} => Instrument::IronXylophone, + Block::SoulSand {} => Instrument::CowBell, + Block::Pumpkin {} => Instrument::Didgeridoo, + Block::EmeraldBlock {} => Instrument::Bit, + Block::HayBlock {} => Instrument::Banjo, + Block::Glowstone { .. } => Instrument::Pling, + _ => Instrument::Harp, + } + } + + pub fn to_sound_id(&self) -> i32 { + match self { + Instrument::Harp => 705, + Instrument::Basedrum => 699, + Instrument::Snare => 708, + Instrument::Hat => 706, + Instrument::Bass => 700, + Instrument::Flute => 703, + Instrument::Bell => 701, + Instrument::Guitar => 704, + Instrument::Chime => 702, + Instrument::Xylophone => 709, + Instrument::IronXylophone => 710, + Instrument::CowBell => 711, + Instrument::Didgeridoo => 712, + Instrument::Bit => 713, + Instrument::Banjo => 714, + Instrument::Pling => 707, + } + } +} + +impl ToString for Instrument { + fn to_string(&self) -> String { + match self { + Instrument::Harp => "harp", + Instrument::Basedrum => "basedrum", + Instrument::Snare => "snare", + Instrument::Hat => "hat", + Instrument::Bass => "bass", + Instrument::Flute => "flute", + Instrument::Bell => "bell", + Instrument::Guitar => "guitar", + Instrument::Chime => "chime", + Instrument::Xylophone => "xylophone", + Instrument::IronXylophone => "iron_xylophone", + Instrument::CowBell => "cow_bell", + Instrument::Didgeridoo => "didgeridoo", + Instrument::Bit => "bit", + Instrument::Banjo => "banjo", + Instrument::Pling => "pling", + } + .to_owned() + } +} + +impl FromStr for Instrument { + type Err = (); + + fn from_str(s: &str) -> Result { + Ok(match s { + "harp" => Instrument::Harp, + "basedrum" => Instrument::Basedrum, + "snare" => Instrument::Snare, + "hat" => Instrument::Hat, + "bass" => Instrument::Bass, + "flute" => Instrument::Flute, + "bell" => Instrument::Bell, + "guitar" => Instrument::Guitar, + "chime" => Instrument::Chime, + "xylophone" => Instrument::Xylophone, + "iron_xylophone" => Instrument::IronXylophone, + "cow_bell" => Instrument::CowBell, + "didgeridoo" => Instrument::Didgeridoo, + "bit" => Instrument::Bit, + "banjo" => Instrument::Banjo, + "pling" => Instrument::Pling, + _ => return Err(()), + }) + } +} diff --git a/crates/blocks/src/items.rs b/crates/blocks/src/items.rs index c62ff694..ace60532 100644 --- a/crates/blocks/src/items.rs +++ b/crates/blocks/src/items.rs @@ -353,6 +353,72 @@ items! { from_id(_id): 640 => {}, block: true, }, + NoteBlock { + props: {}, + get_id: 608, + from_id(_id): 608 => {}, + block: true, + }, + Clay { + props: {}, + get_id: 255, + from_id(_id): 255 => {}, + block: true, + }, + GoldBlock { + props: {}, + get_id: 67, + from_id(_id): 67 => {}, + block: true, + }, + PackedIce { + props: {}, + get_id: 390, + from_id(_id): 390 => {}, + block: true, + }, + BoneBlock { + props: {}, + get_id: 449, + from_id(_id): 449 => {}, + block: true, + }, + IronBlock { + props: {}, + get_id: 65, + from_id(_id): 65 => {}, + block: true, + }, + SoulSand { + props: {}, + get_id: 269, + from_id(_id): 269 => {}, + block: true, + }, + Pumpkin { + props: {}, + get_id: 265, + from_id(_id): 265 => {}, + block: true, + }, + EmeraldBlock { + props: {}, + get_id: 317, + from_id(_id): 317 => {}, + block: true, + }, + HayBlock { + props: {}, + get_id: 372, + from_id(_id): 372 => {}, + block: true, + }, + Sand { + props: {}, + get_id: 37, + from_id(_id): 37 => {}, + block: true, + }, Unknown { props: { id: u32 diff --git a/crates/core/src/interaction.rs b/crates/core/src/interaction.rs index 90630196..61f5e4e3 100644 --- a/crates/core/src/interaction.rs +++ b/crates/core/src/interaction.rs @@ -88,6 +88,21 @@ pub fn on_use( } ActionResult::Success } + Block::NoteBlock { + instrument, + note, + powered, + } => { + world.set_block( + pos, + Block::NoteBlock { + instrument, + note: (note + 1) % 25, + powered, + }, + ); + ActionResult::Success + } b if b.has_block_entity() => { // Open container let block_entity = world.get_block_entity(pos); @@ -219,6 +234,21 @@ pub fn get_state_for_placement( powered: false, }, }, + Item::NoteBlock {} => Block::NoteBlock { + instrument: Instrument::Harp, + note: 0, + powered: false, + }, + Item::Clay {} => Block::Clay {}, + Item::GoldBlock {} => Block::GoldBlock {}, + Item::PackedIce {} => Block::PackedIce {}, + Item::BoneBlock {} => Block::BoneBlock {}, + Item::IronBlock {} => Block::IronBlock {}, + Item::SoulSand {} => Block::SoulSand {}, + Item::Pumpkin {} => Block::Pumpkin {}, + Item::EmeraldBlock {} => Block::EmeraldBlock {}, + Item::HayBlock {} => Block::HayBlock {}, + Item::Sand {} => Block::Sand {}, _ => Block::Air {}, }; if is_valid_position(block, world, pos) { diff --git a/crates/core/src/plot/mod.rs b/crates/core/src/plot/mod.rs index e65d6b4e..3a608026 100644 --- a/crates/core/src/plot/mod.rs +++ b/crates/core/src/plot/mod.rs @@ -232,6 +232,32 @@ impl World for PlotWorld { fn pending_tick_at(&mut self, pos: BlockPos) -> bool { self.to_be_ticked.iter().any(|e| e.pos == pos) } + + fn play_sound( + &mut self, + pos: BlockPos, + sound_id: i32, + sound_category: i32, + volume: f32, + pitch: f32, + ) { + // FIXME: We do not know the players location here, so we send the sound packet to all players + // A notchian server would only send to players in hearing distance (volume.clamp(0.0, 1.0) * 16.0) + let sound_effect_data = CSoundEffect { + sound_id, + sound_category, + x: pos.x * 8 + 4, + y: pos.y * 8 + 4, + z: pos.z * 8 + 4, + volume, + pitch, + } + .encode(); + + for player in &self.packet_senders { + player.send_packet(&sound_effect_data); + } + } } impl Plot { diff --git a/crates/core/src/redpiler/backend/direct/compile.rs b/crates/core/src/redpiler/backend/direct/compile.rs index 34f97238..cbc755ce 100644 --- a/crates/core/src/redpiler/backend/direct/compile.rs +++ b/crates/core/src/redpiler/backend/direct/compile.rs @@ -1,7 +1,8 @@ use crate::redpiler::compile_graph::{CompileGraph, LinkType, NodeIdx}; use crate::redpiler::{CompilerOptions, TaskMonitor}; use itertools::Itertools; -use mchprs_blocks::blocks::Block; +use mchprs_blocks::blocks::{Block, Instrument}; +use mchprs_blocks::BlockPos; use mchprs_world::TickEntry; use petgraph::visit::EdgeRef; use petgraph::Direction; @@ -26,6 +27,7 @@ fn compile_node( node_idx: NodeIdx, nodes_len: usize, nodes_map: &FxHashMap, + noteblock_info: &mut Vec<(BlockPos, Instrument, u32)>, stats: &mut FinalGraphStats, ) -> Node { let node = &graph[node_idx]; @@ -114,6 +116,11 @@ fn compile_node( CNodeType::Trapdoor => NodeType::Trapdoor, CNodeType::Wire => NodeType::Wire, CNodeType::Constant => NodeType::Constant, + CNodeType::NoteBlock { instrument, note } => { + let noteblock_id = noteblock_info.len().try_into().unwrap(); + noteblock_info.push((node.block.unwrap().0, *instrument, *note)); + NodeType::NoteBlock { noteblock_id } + } }; Node { @@ -148,7 +155,16 @@ pub fn compile( let mut stats = FinalGraphStats::default(); let nodes = graph .node_indices() - .map(|idx| compile_node(&graph, idx, nodes_len, &nodes_map, &mut stats)) + .map(|idx| { + compile_node( + &graph, + idx, + nodes_len, + &nodes_map, + &mut backend.noteblock_info, + &mut stats, + ) + }) .collect(); stats.nodes_bytes = nodes_len * std::mem::size_of::(); trace!("{:#?}", stats); diff --git a/crates/core/src/redpiler/backend/direct/mod.rs b/crates/core/src/redpiler/backend/direct/mod.rs index 5c817ae6..8a805a42 100644 --- a/crates/core/src/redpiler/backend/direct/mod.rs +++ b/crates/core/src/redpiler/backend/direct/mod.rs @@ -10,9 +10,10 @@ use crate::redpiler::compile_graph::CompileGraph; use crate::redpiler::task_monitor::TaskMonitor; use crate::redpiler::{block_powered_mut, CompilerOptions}; use crate::redstone::bool_to_ss; +use crate::redstone::noteblock; use crate::world::World; use mchprs_blocks::block_entities::BlockEntity; -use mchprs_blocks::blocks::{Block, ComparatorMode}; +use mchprs_blocks::blocks::{Block, ComparatorMode, Instrument}; use mchprs_blocks::BlockPos; use mchprs_world::{TickEntry, TickPriority}; use node::{Node, NodeId, NodeType, Nodes}; @@ -92,12 +93,18 @@ impl TickScheduler { } } +enum Event { + NoteBlockPlay { noteblock_id: u16 }, +} + #[derive(Default)] pub struct DirectBackend { nodes: Nodes, blocks: Vec>, pos_map: FxHashMap, scheduler: TickScheduler, + events: Vec, + noteblock_info: Vec<(BlockPos, Instrument, u32)>, } impl DirectBackend { @@ -139,7 +146,12 @@ impl DirectBackend { *inputs.ss_counts.get_unchecked_mut(new_power as usize) += 1; } - update::update_node(&mut self.scheduler, update_ref, update); + update::update_node( + &mut self.scheduler, + &mut self.events, + &mut self.nodes, + update, + ); } } } @@ -176,6 +188,8 @@ impl JITBackend for DirectBackend { } self.pos_map.clear(); + self.noteblock_info.clear(); + self.events.clear(); } fn on_use_block(&mut self, pos: BlockPos) { @@ -218,6 +232,14 @@ impl JITBackend for DirectBackend { } fn flush(&mut self, world: &mut W, io_only: bool) { + for event in self.events.drain(..) { + match event { + Event::NoteBlockPlay { noteblock_id } => { + let (pos, instrument, note) = self.noteblock_info[noteblock_id as usize]; + noteblock::play_note(world, pos, instrument, note); + } + } + } for (i, node) in self.nodes.inner_mut().iter_mut().enumerate() { let Some((pos, block)) = &mut self.blocks[i] else { continue; @@ -339,6 +361,7 @@ impl fmt::Display for DirectBackend { NodeType::Trapdoor => format!("Trapdoor"), NodeType::Wire => format!("Wire"), NodeType::Constant => format!("Constant({})", node.output_power), + NodeType::NoteBlock { .. } => format!("NoteBlock"), }; let pos = if let Some((pos, _)) = self.blocks[id] { format!("{}, {}, {}", pos.x, pos.y, pos.z) diff --git a/crates/core/src/redpiler/backend/direct/node.rs b/crates/core/src/redpiler/backend/direct/node.rs index 67bf3c72..2caf4842 100644 --- a/crates/core/src/redpiler/backend/direct/node.rs +++ b/crates/core/src/redpiler/backend/direct/node.rs @@ -3,7 +3,7 @@ use smallvec::SmallVec; use std::num::NonZeroU8; use std::ops::{Index, IndexMut}; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NodeId(u32); impl NodeId { @@ -125,6 +125,23 @@ pub enum NodeType { Trapdoor, Wire, Constant, + NoteBlock { + noteblock_id: u16, + }, +} + +impl NodeType { + pub fn is_io_block(self) -> bool { + matches!( + self, + NodeType::Lamp + | NodeType::Button + | NodeType::Lever + | NodeType::Trapdoor + | NodeType::PressurePlate + | NodeType::NoteBlock { .. } + ) + } } #[repr(align(16))] diff --git a/crates/core/src/redpiler/backend/direct/update.rs b/crates/core/src/redpiler/backend/direct/update.rs index 8c1d8054..3a1de99d 100644 --- a/crates/core/src/redpiler/backend/direct/update.rs +++ b/crates/core/src/redpiler/backend/direct/update.rs @@ -4,7 +4,14 @@ use super::node::{NodeId, NodeType}; use super::*; #[inline(always)] -pub(super) fn update_node(scheduler: &mut TickScheduler, node: &mut Node, node_id: NodeId) { +pub(super) fn update_node( + scheduler: &mut TickScheduler, + events: &mut Vec, + nodes: &mut Nodes, + node_id: NodeId, +) { + let node = &mut nodes[node_id]; + match node.ty { NodeType::Repeater { delay, @@ -86,6 +93,15 @@ pub(super) fn update_node(scheduler: &mut TickScheduler, node: &mut Node, node_i node.changed = true; } } + NodeType::NoteBlock { noteblock_id } => { + let should_be_powered = get_bool_input(node); + if node.powered != should_be_powered { + set_node(node, should_be_powered); + if should_be_powered { + events.push(Event::NoteBlockPlay { noteblock_id }); + } + } + } _ => {} // unreachable!("Node {:?} should not be updated!", node.ty), } } diff --git a/crates/core/src/redpiler/compile_graph.rs b/crates/core/src/redpiler/compile_graph.rs index e3b7300d..f733e9ff 100644 --- a/crates/core/src/redpiler/compile_graph.rs +++ b/crates/core/src/redpiler/compile_graph.rs @@ -1,4 +1,4 @@ -use mchprs_blocks::blocks::ComparatorMode; +use mchprs_blocks::blocks::{ComparatorMode, Instrument}; use mchprs_blocks::BlockPos; use petgraph::stable_graph::{NodeIndex, StableGraph}; @@ -23,6 +23,10 @@ pub enum NodeType { Trapdoor, Wire, Constant, + NoteBlock { + instrument: Instrument, + note: u32, + }, } #[derive(Debug, Clone, Default)] diff --git a/crates/core/src/redpiler/mod.rs b/crates/core/src/redpiler/mod.rs index ebadd948..32be653c 100644 --- a/crates/core/src/redpiler/mod.rs +++ b/crates/core/src/redpiler/mod.rs @@ -29,6 +29,7 @@ fn block_powered_mut(block: &mut Block) -> Option<&mut bool> { Block::StonePressurePlate { powered } => powered, Block::RedstoneLamp { lit } => lit, Block::IronTrapdoor { powered, .. } => powered, + Block::NoteBlock { powered, .. } => powered, _ => return None, }) } diff --git a/crates/core/src/redpiler/passes/export_graph.rs b/crates/core/src/redpiler/passes/export_graph.rs index 3244fa20..cbf53a9e 100644 --- a/crates/core/src/redpiler/passes/export_graph.rs +++ b/crates/core/src/redpiler/passes/export_graph.rs @@ -67,6 +67,7 @@ fn convert_node( CNodeType::Trapdoor => NodeType::Trapdoor, CNodeType::Wire => NodeType::Wire, CNodeType::Constant => NodeType::Constant, + CNodeType::NoteBlock { .. } => NodeType::NoteBlock, }, block: node.block.map(|(pos, id)| { ( diff --git a/crates/core/src/redpiler/passes/identify_nodes.rs b/crates/core/src/redpiler/passes/identify_nodes.rs index fe61c5f5..ae0f5ab8 100644 --- a/crates/core/src/redpiler/passes/identify_nodes.rs +++ b/crates/core/src/redpiler/passes/identify_nodes.rs @@ -12,7 +12,7 @@ use crate::redpiler::compile_graph::{ Annotations, CompileGraph, CompileNode, NodeIdx, NodeState, NodeType, }; use crate::redpiler::{CompilerInput, CompilerOptions}; -use crate::redstone::{self, comparator}; +use crate::redstone::{self, comparator, noteblock}; use crate::world::{for_each_block_optimized, World}; use itertools::Itertools; use mchprs_blocks::block_entities::BlockEntity; @@ -89,7 +89,10 @@ fn for_pos( ty, NodeType::Button | NodeType::Lever | NodeType::PressurePlate ); - let is_output = matches!(ty, NodeType::Trapdoor | NodeType::Lamp); + let is_output = matches!( + ty, + NodeType::Trapdoor | NodeType::Lamp | NodeType::NoteBlock { .. } + ); // || matches!(block, Block::RedstoneWire { wire } if wire::is_dot(wire)); if ignore_wires && ty == NodeType::Wire && !(is_input | is_output) { @@ -154,6 +157,17 @@ fn identify_block( } Block::IronTrapdoor { powered, .. } => (NodeType::Trapdoor, NodeState::simple(powered)), Block::RedstoneBlock {} => (NodeType::Constant, NodeState::ss(15)), + Block::NoteBlock { + instrument: _, + note, + powered, + } if noteblock::is_noteblock_unblocked(world, pos) => { + let instrument = noteblock::get_noteblock_instrument(world, pos); + ( + NodeType::NoteBlock { instrument, note }, + NodeState::simple(powered), + ) + } block if comparator::has_override(block) => ( NodeType::Constant, NodeState::ss(comparator::get_override(block, world, pos)), diff --git a/crates/core/src/redpiler/passes/input_search.rs b/crates/core/src/redpiler/passes/input_search.rs index 969566db..c57a146a 100644 --- a/crates/core/src/redpiler/passes/input_search.rs +++ b/crates/core/src/redpiler/passes/input_search.rs @@ -324,7 +324,7 @@ impl<'a, W: World> InputSearchState<'a, W> { Block::RedstoneWire { .. } => { self.search_wire(id, pos, LinkType::Default, 0); } - Block::RedstoneLamp { .. } | Block::IronTrapdoor { .. } => { + Block::RedstoneLamp { .. } | Block::IronTrapdoor { .. } | Block::NoteBlock { .. } => { for face in &BlockFace::values() { let neighbor_pos = pos.offset(*face); let neighbor_block = self.world.get_block(neighbor_pos); diff --git a/crates/core/src/redstone/mod.rs b/crates/core/src/redstone/mod.rs index 9f0e0ab0..dc64db94 100644 --- a/crates/core/src/redstone/mod.rs +++ b/crates/core/src/redstone/mod.rs @@ -3,6 +3,7 @@ //! scenerio (i.e. regular buiding) pub mod comparator; +pub mod noteblock; pub mod repeater; pub mod wire; @@ -226,6 +227,32 @@ pub fn update(block: Block, world: &mut impl World, pos: BlockPos) { world.set_block(pos, new_block); } } + Block::NoteBlock { + instrument: _instrument, + note, + .. + } => { + let should_be_powered = redstone_lamp_should_be_lit(world, pos); + // We need to recheck if the live version of the block is powered, + // because the supplied block is cached and could be outdated + let Block::NoteBlock { powered, .. } = world.get_block(pos) else { + unreachable!("Underlying block changed, this should never happen") + }; + if powered != should_be_powered { + // Hack: Update the instrument only just before the noteblock is updated + let instrument = noteblock::get_noteblock_instrument(world, pos); + let new_block = Block::NoteBlock { + instrument, + note, + powered: should_be_powered, + }; + + if should_be_powered && noteblock::is_noteblock_unblocked(world, pos) { + noteblock::play_note(world, pos, instrument, note); + } + world.set_block(pos, new_block); + } + } _ => {} } } diff --git a/crates/core/src/redstone/noteblock.rs b/crates/core/src/redstone/noteblock.rs new file mode 100644 index 00000000..f19bc966 --- /dev/null +++ b/crates/core/src/redstone/noteblock.rs @@ -0,0 +1,30 @@ +use mchprs_blocks::blocks::{Block, Instrument}; +use mchprs_blocks::{BlockFace, BlockPos}; + +use crate::world::World; + +// LUT generated via f32::powf(2.0, (note as f32 - 12.0) / 12.0) +// This is hardcoded because at this point floating point operations are not allowed in const contexts +const PITCHES_TABLE: [f32; 25] = [ + 0.5, 0.5297315, 0.561231, 0.59460354, 0.62996054, 0.6674199, 0.70710677, 0.74915355, 0.7937005, + 0.8408964, 0.8908987, 0.9438743, 1.0, 1.0594631, 1.122462, 1.1892071, 1.2599211, 1.3348398, + 1.4142135, 1.4983071, 1.587401, 1.6817929, 1.7817974, 1.8877486, 2.0, +]; + +pub fn is_noteblock_unblocked(world: &impl World, pos: BlockPos) -> bool { + matches!(world.get_block(pos.offset(BlockFace::Top)), Block::Air {}) +} + +pub fn get_noteblock_instrument(world: &impl World, pos: BlockPos) -> Instrument { + Instrument::from_block_below(world.get_block(pos.offset(BlockFace::Bottom))) +} + +pub fn play_note(world: &mut impl World, pos: BlockPos, instrument: Instrument, note: u32) { + world.play_sound( + pos, + instrument.to_sound_id(), + 2, // Sound Caregory ID for Records + 3.0, + PITCHES_TABLE[note as usize], + ); +} diff --git a/crates/core/src/world/mod.rs b/crates/core/src/world/mod.rs index afcd93a0..7f44b57e 100644 --- a/crates/core/src/world/mod.rs +++ b/crates/core/src/world/mod.rs @@ -53,6 +53,17 @@ pub trait World { fn is_cursed(&self) -> bool { false } + + #[allow(unused_variables)] + fn play_sound( + &mut self, + pos: BlockPos, + sound_id: i32, + sound_category: i32, + volume: f32, + pitch: f32, + ) { + } } // TODO: I have no idea how to deduplicate this in a sane way diff --git a/crates/redpiler_graph/src/lib.rs b/crates/redpiler_graph/src/lib.rs index 8bff9a61..aa112d21 100644 --- a/crates/redpiler_graph/src/lib.rs +++ b/crates/redpiler_graph/src/lib.rs @@ -41,6 +41,7 @@ pub enum NodeType { Trapdoor, Wire, Constant, + NoteBlock, } #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, Debug)]