diff --git a/crates/blocks/src/blocks/props.rs b/crates/blocks/src/blocks/props.rs index 2469aaf4..44ebadf0 100644 --- a/crates/blocks/src/blocks/props.rs +++ b/crates/blocks/src/blocks/props.rs @@ -542,3 +542,7 @@ impl FromStr for Instrument { }) } } + +pub fn noteblock_note_to_pitch(note: u32) -> f32 { + f32::powf(2.0, (note as f32 - 12.0) / 12.0) +} diff --git a/crates/core/src/redpiler/backend/direct.rs b/crates/core/src/redpiler/backend/direct.rs index fcc75203..ee1b61bd 100644 --- a/crates/core/src/redpiler/backend/direct.rs +++ b/crates/core/src/redpiler/backend/direct.rs @@ -5,7 +5,7 @@ use crate::redpiler::compile_graph::{CompileGraph, LinkType, NodeIdx}; use crate::redpiler::{block_powered_mut, bool_to_ss}; use crate::world::World; use mchprs_blocks::block_entities::BlockEntity; -use mchprs_blocks::blocks::{Block, ComparatorMode}; +use mchprs_blocks::blocks::{noteblock_note_to_pitch, Block, ComparatorMode, Instrument}; use mchprs_blocks::BlockPos; use mchprs_world::{TickEntry, TickPriority}; use nodes::{NodeId, Nodes}; @@ -28,7 +28,7 @@ mod nodes { use super::Node; use std::ops::{Index, IndexMut}; - #[derive(Debug, Copy, Clone)] + #[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct NodeId(u32); impl NodeId { @@ -136,6 +136,7 @@ enum NodeType { Trapdoor, Wire, Constant, + NoteBlock, } impl NodeType { @@ -147,6 +148,7 @@ impl NodeType { | NodeType::Lever | NodeType::Trapdoor | NodeType::PressurePlate + | NodeType::NoteBlock { .. } ) } } @@ -185,6 +187,7 @@ impl Node { node_idx: NodeIdx, nodes_len: usize, nodes_map: &FxHashMap, + noteblock_map: &mut FxHashMap, stats: &mut FinalGraphStats, ) -> Self { let node = &graph[node_idx]; @@ -261,6 +264,16 @@ impl Node { CNodeType::Trapdoor => NodeType::Trapdoor, CNodeType::Wire => NodeType::Wire, CNodeType::Constant => NodeType::Constant, + CNodeType::NoteBlock { instrument, note } => { + let idx = nodes_map[&node_idx]; + assert!(idx < nodes_len); + let idx = unsafe { + // Safety: bounds checked + NodeId::from_index(idx) + }; + noteblock_map.insert(idx, (node.block.unwrap().0, instrument, note)); + NodeType::NoteBlock + } }; Node { @@ -360,12 +373,18 @@ impl TickScheduler { } } +enum Event { + NoteBlockPlay(NodeId), +} + #[derive(Default)] pub struct DirectBackend { nodes: Nodes, blocks: Vec>, pos_map: FxHashMap, scheduler: TickScheduler, + events: Vec, + noteblock_map: FxHashMap, } impl DirectBackend { @@ -407,7 +426,12 @@ impl DirectBackend { *inputs.ss_counts.get_unchecked_mut(new_power as usize) += 1; } - update_node(&mut self.scheduler, &mut self.nodes, update); + update_node( + &mut self.scheduler, + &mut self.events, + &mut self.nodes, + update, + ); } } } @@ -575,7 +599,16 @@ impl JITBackend for DirectBackend { let mut stats = FinalGraphStats::default(); let nodes = graph .node_indices() - .map(|idx| Node::from_compile_node(&graph, idx, nodes_len, &nodes_map, &mut stats)) + .map(|idx| { + Node::from_compile_node( + &graph, + idx, + nodes_len, + &nodes_map, + &mut self.noteblock_map, + &mut stats, + ) + }) .collect(); stats.nodes_bytes = nodes_len * std::mem::size_of::(); trace!("{:#?}", stats); @@ -604,6 +637,20 @@ impl JITBackend for DirectBackend { } fn flush(&mut self, world: &mut W, io_only: bool) { + for event in self.events.drain(..) { + match event { + Event::NoteBlockPlay(node_id) => { + let &(pos, instrument, note) = self.noteblock_map.get(&node_id).unwrap(); + world.play_sound( + pos, + instrument.to_sound_id(), + 2, // Sound Caregory ID for Records + 3.0, + noteblock_note_to_pitch(note), + ); + } + } + } for (i, node) in self.nodes.inner_mut().iter_mut().enumerate() { let Some((pos, block)) = &mut self.blocks[i] else { continue; @@ -679,7 +726,12 @@ fn get_all_input(node: &Node) -> (u8, u8) { } #[inline(always)] -fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId) { +fn update_node( + scheduler: &mut TickScheduler, + sound_events: &mut Vec, + nodes: &mut Nodes, + node_id: NodeId, +) { let node = &nodes[node_id]; match node.ty { @@ -773,6 +825,16 @@ fn update_node(scheduler: &mut TickScheduler, nodes: &mut Nodes, node_id: NodeId set_node(node, should_be_powered); } } + NodeType::NoteBlock { .. } => { + let should_be_powered = get_bool_input(node); + if node.powered != should_be_powered { + let node = &mut nodes[node_id]; + set_node(node, should_be_powered); + if should_be_powered { + sound_events.push(Event::NoteBlockPlay(node_id)); + } + } + } NodeType::Wire => { let (input_power, _) = get_all_input(node); if node.output_power != input_power { diff --git a/crates/core/src/redpiler/compile_graph.rs b/crates/core/src/redpiler/compile_graph.rs index e9487250..f54966e7 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}; @@ -16,11 +16,15 @@ pub enum NodeType { Trapdoor, Wire, Constant, + NoteBlock { instrument: Instrument, note: u32 }, } impl NodeType { pub fn is_output(self) -> bool { - matches!(self, NodeType::Lamp | NodeType::Trapdoor) + matches!( + self, + NodeType::Lamp | NodeType::Trapdoor | NodeType::NoteBlock { .. } + ) } } diff --git a/crates/core/src/redpiler/passes/export_graph.rs b/crates/core/src/redpiler/passes/export_graph.rs index 27c0302c..cb887790 100644 --- a/crates/core/src/redpiler/passes/export_graph.rs +++ b/crates/core/src/redpiler/passes/export_graph.rs @@ -55,6 +55,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 2d0251d7..940c7449 100644 --- a/crates/core/src/redpiler/passes/identify_nodes.rs +++ b/crates/core/src/redpiler/passes/identify_nodes.rs @@ -13,8 +13,8 @@ use crate::redpiler::{CompilerInput, CompilerOptions}; use crate::redstone; use crate::world::{for_each_block_optimized, World}; use mchprs_blocks::block_entities::BlockEntity; -use mchprs_blocks::blocks::{Block, RedstoneComparator, RedstoneRepeater}; -use mchprs_blocks::BlockPos; +use mchprs_blocks::blocks::{Block, Instrument, RedstoneComparator, RedstoneRepeater}; +use mchprs_blocks::{BlockFace, BlockPos}; pub struct IdentifyNodes; @@ -113,6 +113,18 @@ fn identify_block( } Block::IronTrapdoor { powered, .. } => (NodeType::Trapdoor, NodeState::simple(powered)), Block::RedstoneBlock {} => (NodeType::Constant, NodeState::ss(15)), + Block::NoteBlock { + instrument: _, + note, + powered, + } if world.get_block(pos.offset(BlockFace::Top)) == (Block::Air {}) => { + let below = world.get_block(pos.offset(BlockFace::Bottom)); + let instrument = Instrument::from_block_below(below); + ( + NodeType::NoteBlock { instrument, note }, + NodeState::simple(powered), + ) + } block if redstone::has_comparator_override(block) => ( NodeType::Constant, NodeState::ss(redstone::get_comparator_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 9f03f966..9bd4c403 100644 --- a/crates/core/src/redpiler/passes/input_search.rs +++ b/crates/core/src/redpiler/passes/input_search.rs @@ -334,7 +334,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 94f483fc..299d8416 100644 --- a/crates/core/src/redstone/mod.rs +++ b/crates/core/src/redstone/mod.rs @@ -8,7 +8,7 @@ pub mod wire; use crate::world::World; use mchprs_blocks::block_entities::BlockEntity; -use mchprs_blocks::blocks::{Block, ButtonFace, Instrument, LeverFace}; +use mchprs_blocks::blocks::{noteblock_note_to_pitch, Block, ButtonFace, Instrument, LeverFace}; use mchprs_blocks::{BlockDirection, BlockFace, BlockPos}; use mchprs_world::TickPriority; @@ -233,8 +233,14 @@ pub fn update(block: Block, world: &mut impl World, pos: BlockPos) { powered: should_be_powered, }; - world.set_block(pos, new_block); + // TODO: There is a weird double activation bug, so we need to check if block is not already powered + let is_not_powered = matches!( + world.get_block(pos), + Block::NoteBlock { powered: false, .. } + ); + if should_be_powered + && is_not_powered && world.get_block(pos.offset(BlockFace::Top)) == (Block::Air {}) { world.play_sound( @@ -242,9 +248,10 @@ pub fn update(block: Block, world: &mut impl World, pos: BlockPos) { instrument.to_sound_id(), 2, // Sound Caregory ID for Records 3.0, - f32::powf(2.0, (note as f32 - 12.0) / 12.0), + noteblock_note_to_pitch(note), ); } + world.set_block(pos, new_block); } } _ => {} diff --git a/crates/core/src/world/mod.rs b/crates/core/src/world/mod.rs index dea0ff57..7f44b57e 100644 --- a/crates/core/src/world/mod.rs +++ b/crates/core/src/world/mod.rs @@ -54,13 +54,14 @@ pub trait World { false } + #[allow(unused_variables)] fn play_sound( &mut self, - _pos: BlockPos, - _sound_id: i32, - _sound_category: i32, - _volume: f32, - _pitch: f32, + pos: BlockPos, + sound_id: i32, + sound_category: i32, + volume: f32, + pitch: f32, ) { } } 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)]