135 lines
5.2 KiB
Rust
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)
|
|
}
|