diff --git a/crates/pumpkin/src/block/blocks/creaking_heart.rs b/crates/pumpkin/src/block/blocks/creaking_heart.rs new file mode 100644 index 000000000..ffb982358 --- /dev/null +++ b/crates/pumpkin/src/block/blocks/creaking_heart.rs @@ -0,0 +1,136 @@ +use std::sync::Arc; + +use pumpkin_data::block_properties::{ + Axis, BlockProperties, CreakingHeartLikeProperties, CreakingHeartState, +}; +use pumpkin_data::item::Item; +use pumpkin_data::item_stack::ItemStack; +use pumpkin_data::sound::{Sound, SoundCategory}; +use pumpkin_data::{Block, BlockStateId}; +use pumpkin_macros::pumpkin_block; +use pumpkin_world::world::BlockFlags; + +use crate::block::entities::creaking_heart::CreakingHeartBlockEntity; +use crate::block::{ + BlockBehaviour, BlockFuture, BrokenArgs, OnNeighborUpdateArgs, OnPlaceArgs, PlacedArgs, +}; + +#[pumpkin_block("minecraft:creaking_heart")] +pub struct CreakingHeartBlock; + +impl CreakingHeartBlock { + fn is_pale_oak_log(block: &Block) -> bool { + block.name == "pale_oak_log" || block.name == "stripped_pale_oak_log" + } + + fn check_active_logs( + world: &dyn pumpkin_world::world::BlockAccessor, + pos: &pumpkin_util::math::position::BlockPos, + axis: Axis, + ) -> bool { + let (pos_a, pos_b) = match axis { + Axis::X => (pos.west(), pos.east()), + Axis::Y => (pos.down(), pos.up()), + Axis::Z => (pos.north(), pos.south()), + }; + + let state_a = world.get_block_state(&pos_a); + let state_b = world.get_block_state(&pos_b); + + let block_a = Block::from_state_id(state_a.id); + let block_b = Block::from_state_id(state_b.id); + + Self::is_pale_oak_log(block_a) && Self::is_pale_oak_log(block_b) + } +} + +impl BlockBehaviour for CreakingHeartBlock { + fn on_place<'a>(&'a self, args: OnPlaceArgs<'a>) -> BlockFuture<'a, BlockStateId> { + Box::pin(async move { + let mut props = + CreakingHeartLikeProperties::from_state_id(args.block.default_state.id, args.block); + props.axis = match args.direction { + pumpkin_data::BlockDirection::North | pumpkin_data::BlockDirection::South => { + Axis::Z + } + pumpkin_data::BlockDirection::East | pumpkin_data::BlockDirection::West => Axis::X, + pumpkin_data::BlockDirection::Up | pumpkin_data::BlockDirection::Down => Axis::Y, + }; + props.creaking_heart_state = CreakingHeartState::Uprooted; + props.to_state_id(args.block) + }) + } + + fn placed<'a>(&'a self, args: PlacedArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let entity = CreakingHeartBlockEntity::new(*args.position); + args.world.add_block_entity(Arc::new(entity)); + + let state_id = args.world.get_block_state_id(args.position); + let mut props = CreakingHeartLikeProperties::from_state_id(state_id, args.block); + + if Self::check_active_logs(args.world.as_ref(), args.position, props.axis) { + props.creaking_heart_state = CreakingHeartState::Dormant; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + + args.world.play_sound( + Sound::BlockCreakingHeartSpawn, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } + }) + } + + fn on_neighbor_update<'a>(&'a self, args: OnNeighborUpdateArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + let state_id = args.world.get_block_state_id(args.position); + let mut props = CreakingHeartLikeProperties::from_state_id(state_id, args.block); + + let active_logs = + Self::check_active_logs(args.world.as_ref(), args.position, props.axis); + let new_state = if active_logs { + if props.creaking_heart_state == CreakingHeartState::Uprooted { + args.world.play_sound( + Sound::BlockCreakingHeartSpawn, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + } + CreakingHeartState::Dormant + } else { + CreakingHeartState::Uprooted + }; + + if props.creaking_heart_state != new_state { + props.creaking_heart_state = new_state; + args.world + .set_block_state( + args.position, + props.to_state_id(args.block), + BlockFlags::NOTIFY_ALL, + ) + .await; + } + }) + } + + fn broken<'a>(&'a self, args: BrokenArgs<'a>) -> BlockFuture<'a, ()> { + Box::pin(async move { + args.world.play_sound( + Sound::BlockCreakingHeartBreak, + SoundCategory::Blocks, + &args.position.to_f64(), + ); + args.world + .drop_stack(args.position, ItemStack::new(1, &Item::CREAKING_HEART)) + .await; + }) + } +} diff --git a/crates/pumpkin/src/block/blocks/mod.rs b/crates/pumpkin/src/block/blocks/mod.rs index de3ff8698..1998cb150 100644 --- a/crates/pumpkin/src/block/blocks/mod.rs +++ b/crates/pumpkin/src/block/blocks/mod.rs @@ -110,6 +110,7 @@ pub mod coral; pub mod abstract_wall_mounting; pub mod beacon; +pub mod creaking_heart; pub mod decorated_pot; pub mod trial_spawner; pub mod vault; diff --git a/crates/pumpkin/src/block/registry.rs b/crates/pumpkin/src/block/registry.rs index af10a00e3..d8b2bfa3c 100644 --- a/crates/pumpkin/src/block/registry.rs +++ b/crates/pumpkin/src/block/registry.rs @@ -23,6 +23,7 @@ use crate::block::blocks::conduit::ConduitBlock; use crate::block::blocks::coral::coral_block::CoralBlock; use crate::block::blocks::coral::coral_fan::CoralFanBlock; use crate::block::blocks::coral::coral_plant::CoralPlantBlock; +use crate::block::blocks::creaking_heart::CreakingHeartBlock; use crate::block::blocks::decorated_pot::DecoratedPotBlock; use crate::block::blocks::dirt_path::DirtPathBlock; use crate::block::blocks::doors::DoorBlock; @@ -254,6 +255,7 @@ pub fn default_registry() -> Arc { manager.register(TrialSpawnerBlock); manager.register(VaultBlock); manager.register(DecoratedPotBlock); + manager.register(CreakingHeartBlock); manager.register(BushBlock); manager.register(FlowerBlock); manager.register(PotatoBlock);