use pumpkin_data::{ Block, BlockDirection, BlockState, BlockStateId, block_properties::BrownMushroomBlockLikeProperties, tag::{self, Taggable}, }; use pumpkin_macros::pumpkin_block; use pumpkin_util::math::position::BlockPos; use pumpkin_world::{ tick::TickPriority, world::{BlockAccessor, BlockFlags}, }; use crate::block::{ BlockBehaviour, CanPlaceAtArgs, GetStateForNeighborUpdateArgs, OnPlaceArgs, OnScheduledTickArgs, PathComputationType, }; #[pumpkin_block("minecraft:chorus_plant")] pub struct ChorusPlantBlock; impl BlockBehaviour for ChorusPlantBlock { fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId { // Compute all 6 face connections immediately so the placed block visually connects to its neighbors. get_state_with_connections(args.world, args.block, args.position) } fn can_place_at(&self, args: CanPlaceAtArgs<'_>) -> bool { can_survive(args.block_accessor, args.position) } fn get_state_for_neighbor_update( &self, args: GetStateForNeighborUpdateArgs<'_>, ) -> BlockStateId { if !can_survive(args.world, args.position) { // Schedule delayed destruction so the whole plant cascades down. args.world .schedule_block_tick(args.block, *args.position, 1, TickPriority::Normal); return args.state_id; } // Update the single face connection for the direction that changed. let neighbor_block = args.world.get_block(args.neighbor_position); let connect = neighbor_block == &Block::CHORUS_PLANT || neighbor_block == &Block::CHORUS_FLOWER || (args.direction == BlockDirection::Down && neighbor_block.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_PLANT)); let mut props = BrownMushroomBlockLikeProperties::from_state_id(args.state_id); match args.direction { BlockDirection::Down => props.down = connect, BlockDirection::Up => props.up = connect, BlockDirection::North => props.north = connect, BlockDirection::South => props.south = connect, BlockDirection::East => props.east = connect, BlockDirection::West => props.west = connect, } props.to_state_id(args.block) } fn on_scheduled_tick(&self, args: OnScheduledTickArgs<'_>) { // Destroy if unsupported; breaking propagates neighbor-update callbacks // to connected chorus blocks, which schedule their own ticks. if !can_survive(args.world.as_ref(), args.position) { args.world .break_block(args.position, None, BlockFlags::empty()); } } fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool { false } } #[must_use] pub fn get_state_with_connections( block_accessor: &dyn BlockAccessor, block: &Block, pos: &BlockPos, ) -> BlockStateId { let plant_id = Block::CHORUS_PLANT.id; let flower_id = Block::CHORUS_FLOWER.id; let supports = &tag::Block::MINECRAFT_SUPPORTS_CHORUS_PLANT; let connects = |b: &Block| b.id == plant_id || b.id == flower_id; let block_down = block_accessor.get_block(&pos.down()); let block_up = block_accessor.get_block(&pos.up()); let block_north = block_accessor.get_block(&pos.offset(BlockDirection::North.to_offset())); let block_east = block_accessor.get_block(&pos.offset(BlockDirection::East.to_offset())); let block_south = block_accessor.get_block(&pos.offset(BlockDirection::South.to_offset())); let block_west = block_accessor.get_block(&pos.offset(BlockDirection::West.to_offset())); let props = BrownMushroomBlockLikeProperties { down: connects(block_down) || block_down.has_tag(supports), up: connects(block_up), north: connects(block_north), east: connects(block_east), south: connects(block_south), west: connects(block_west), }; props.to_state_id(block) } fn can_survive(block_accessor: &dyn BlockAccessor, pos: &BlockPos) -> bool { let below_pos = pos.down(); let above_pos = pos.up(); let (block_below, state_below) = block_accessor.get_block_and_state(&below_pos); let state_above = block_accessor.get_block_state(&above_pos); // A horizontal branch is invalid when both the block immediately above and // below this one are non-air (enclosed inside something). let block_above_or_below = !state_above.is_air() && !state_below.is_air(); for dir in BlockDirection::horizontal() { let neighbor_pos = pos.offset(dir.to_offset()); let neighbor_block = block_accessor.get_block(&neighbor_pos); if neighbor_block == &Block::CHORUS_PLANT { if block_above_or_below { return false; } let neighbor_below = block_accessor.get_block(&neighbor_pos.down()); if neighbor_below == &Block::CHORUS_PLANT || neighbor_below.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_PLANT) { return true; } } } block_below == &Block::CHORUS_PLANT || block_below.has_tag(&tag::Block::MINECRAFT_SUPPORTS_CHORUS_PLANT) }