Pumpkin/crates/pumpkin/src/block/blocks/plant/chorus_plant.rs
2026-09-02 15:16:42 +02:00

135 lines
5.2 KiB
Rust

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)
}