use crate::block::blocks::redstone::block_receives_redstone_power; use crate::block::registry::BlockActionResult; use crate::block::{ BlockBehaviour, NormalUseArgs, OnNeighborUpdateArgs, OnPlaceArgs, PathComputationType, }; use crate::entity::EntityBase; use crate::entity::player::Player; use crate::world::World; use pumpkin_data::BlockDirection; use pumpkin_data::block_properties::Half; use pumpkin_data::sound::{Sound, SoundCategory}; use pumpkin_data::tag::Taggable; use pumpkin_data::{Block, BlockState, BlockStateId, tag}; use pumpkin_macros::pumpkin_block_from_tag; use pumpkin_util::math::position::BlockPos; use pumpkin_world::world::BlockFlags; use std::sync::Arc; type TrapDoorProperties = pumpkin_data::block_properties::OakTrapdoorLikeProperties; fn toggle_trapdoor(player: &Player, world: &Arc, block_pos: &BlockPos) { let (block, block_state) = world.get_block_and_state_id(block_pos); let mut trapdoor_props = TrapDoorProperties::from_state_id(block_state); trapdoor_props.open = !trapdoor_props.open; world.play_block_sound_expect( player, get_sound(block, trapdoor_props.open), SoundCategory::Blocks, *block_pos, ); world.set_block_state( block_pos, trapdoor_props.to_state_id(block), BlockFlags::NOTIFY_LISTENERS, ); } fn can_open_trapdoor(block: &Block) -> bool { if block == &Block::IRON_TRAPDOOR { return false; } true } fn get_sound(block: &Block, open: bool) -> Sound { if open { if block.has_tag(&tag::Block::MINECRAFT_WOODEN_TRAPDOORS) { Sound::BlockWoodenTrapdoorOpen } else if block == &Block::IRON_TRAPDOOR { Sound::BlockIronTrapdoorOpen } else { Sound::BlockCopperTrapdoorOpen } } else if block.has_tag(&tag::Block::MINECRAFT_WOODEN_TRAPDOORS) { Sound::BlockWoodenTrapdoorClose } else if block == &Block::IRON_TRAPDOOR { Sound::BlockIronTrapdoorClose } else { Sound::BlockCopperTrapdoorClose } } #[pumpkin_block_from_tag("minecraft:trapdoors")] pub struct TrapDoorBlock; impl BlockBehaviour for TrapDoorBlock { fn normal_use(&self, args: NormalUseArgs<'_>) -> BlockActionResult { { if !can_open_trapdoor(args.block) { return BlockActionResult::Pass; } toggle_trapdoor(args.player, args.world, args.position); BlockActionResult::Success } } fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { let mut trapdoor_props = TrapDoorProperties::default(args.block); trapdoor_props.waterlogged = args.replacing.water_source(); let powered = block_receives_redstone_power(args.world, args.position); let player_facing = args.player.get_entity().get_horizontal_facing(); // Correct facing logic using Option unwrap let facing = args .direction .to_horizontal_facing() .unwrap_or(player_facing); trapdoor_props.facing = facing; trapdoor_props.half = match args.direction { BlockDirection::Up => Half::Top, BlockDirection::Down => Half::Bottom, _ => match args.use_item_on.cursor_pos.y { 0.0..0.5 => Half::Bottom, _ => Half::Top, }, }; trapdoor_props.powered = powered; trapdoor_props.open = powered; trapdoor_props.to_state_id(args.block) } fn on_neighbor_update(&self, args: OnNeighborUpdateArgs<'_>) { { let block_state = args.world.get_block_state(args.position); let mut trapdoor_props = TrapDoorProperties::from_state_id(block_state.id); let powered = block_receives_redstone_power(args.world, args.position); if powered != trapdoor_props.powered { trapdoor_props.powered = !trapdoor_props.powered; if powered != trapdoor_props.open { trapdoor_props.open = trapdoor_props.powered; args.world.play_block_sound( get_sound(args.block, powered), SoundCategory::Blocks, *args.position, ); } } args.world.set_block_state( args.position, trapdoor_props.to_state_id(args.block), BlockFlags::NOTIFY_LISTENERS, ); } } fn is_pathfindable(&self, state: &BlockState, computation_type: PathComputationType) -> bool { let props = TrapDoorProperties::from_state_id(state.id); match computation_type { PathComputationType::Land | PathComputationType::Air => props.open, PathComputationType::Water => props.waterlogged, } } }