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

213 lines
7.7 KiB
Rust

use crate::block::{
BlockBehaviour, GetStateForNeighborUpdateArgs, OnPlaceArgs, PathComputationType,
};
use crate::world::World;
use pumpkin_data::block_properties::EastWall;
use pumpkin_data::block_properties::HorizontalFacing;
use pumpkin_data::block_properties::NorthWall;
use pumpkin_data::block_properties::SouthWall;
use pumpkin_data::block_properties::WestWall;
use pumpkin_data::tag::Taggable;
use pumpkin_data::{Block, BlockDirection, BlockState, BlockStateId, tag};
use pumpkin_macros::pumpkin_block_from_tag;
use pumpkin_util::math::position::BlockPos;
type FenceGateProperties = pumpkin_data::block_properties::OakFenceGateLikeProperties;
type FenceLikeProperties = pumpkin_data::block_properties::OakFenceLikeProperties;
type WallProperties = pumpkin_data::block_properties::ResinBrickWallLikeProperties;
#[pumpkin_block_from_tag("minecraft:walls")]
pub struct WallBlock;
impl BlockBehaviour for WallBlock {
fn on_place(&self, args: OnPlaceArgs<'_>) -> BlockStateId {
let mut wall_props = WallProperties::default(args.block);
wall_props.waterlogged = args.replacing.water_source();
compute_wall_state(wall_props, args.world, args.block, args.position)
}
fn get_state_for_neighbor_update(
&self,
args: GetStateForNeighborUpdateArgs<'_>,
) -> BlockStateId {
let wall_props = WallProperties::from_state_id(args.state_id);
compute_wall_state(wall_props, args.world, args.block, args.position)
}
fn is_pathfindable(&self, _state: &BlockState, _computation_type: PathComputationType) -> bool {
false
}
}
pub fn compute_wall_state(
mut wall_props: WallProperties,
world: &World,
block: &Block,
block_pos: &BlockPos,
) -> BlockStateId {
let (block_above, block_above_state) = world.get_block_and_state(&block_pos.up());
for direction in HorizontalFacing::all() {
let other_block_pos = block_pos.offset(direction.to_offset());
let (other_block, other_block_state) = world.get_block_and_state(&other_block_pos);
let connected = is_connected(block, direction, other_block, other_block_state);
let shape = if connected {
let raise = if block_above_state.is_full_cube() {
true
} else if block_above.has_tag(&tag::Block::MINECRAFT_WALLS) {
let other_props = WallProperties::from_state_id(block_above_state.id);
match direction {
HorizontalFacing::North => other_props.north != NorthWall::None,
HorizontalFacing::South => other_props.south != SouthWall::None,
HorizontalFacing::East => other_props.east != EastWall::None,
HorizontalFacing::West => other_props.west != WestWall::None,
}
} else if block_above.has_tag(&tag::Block::C_GLASS_PANES)
|| block_above.has_tag(&tag::Block::MINECRAFT_FENCES)
|| block_above == &Block::IRON_BARS
{
let other_props = FenceLikeProperties::from_state_id(block_above_state.id);
match direction {
HorizontalFacing::North => other_props.north,
HorizontalFacing::South => other_props.south,
HorizontalFacing::East => other_props.east,
HorizontalFacing::West => other_props.west,
}
} else if block_above.has_tag(&tag::Block::MINECRAFT_FENCE_GATES) {
let other_props = FenceGateProperties::from_state_id(block_above_state.id);
// gate is perp to connected direction
let perpendicular_gate = direction == other_props.facing.rotate_clockwise()
|| direction == other_props.facing.rotate_counter_clockwise();
perpendicular_gate && !other_props.open
} else {
false
};
if raise {
WallShape::Tall
} else {
WallShape::Low
}
} else {
WallShape::None
};
match direction {
HorizontalFacing::North => wall_props.north = shape.into(),
HorizontalFacing::South => wall_props.south = shape.into(),
HorizontalFacing::East => wall_props.east = shape.into(),
HorizontalFacing::West => wall_props.west = shape.into(),
}
}
let connected_north_south = wall_props.north != NorthWall::None
&& wall_props.south != SouthWall::None
&& wall_props.east == EastWall::None
&& wall_props.west == WestWall::None;
let connected_east_west = wall_props.north == NorthWall::None
&& wall_props.south == SouthWall::None
&& wall_props.east != EastWall::None
&& wall_props.west != WestWall::None;
let cross = wall_props.north != NorthWall::None
&& wall_props.south != SouthWall::None
&& wall_props.east != EastWall::None
&& wall_props.west != WestWall::None;
wall_props.up = if !(cross || connected_north_south || connected_east_west) {
true
} else if block_above.has_tag(&tag::Block::MINECRAFT_WALLS) {
let other_props = WallProperties::from_state_id(block_above_state.id);
other_props.up
} else if block_above.has_tag(&tag::Block::MINECRAFT_FENCE_GATES) {
let other_props = FenceGateProperties::from_state_id(block_above_state.id);
if other_props.open {
false
} else {
match other_props.facing {
HorizontalFacing::East | HorizontalFacing::West => connected_east_west,
HorizontalFacing::South | HorizontalFacing::North => connected_north_south,
}
}
} else {
false
};
wall_props.to_state_id(block)
}
fn is_connected(
block: &Block,
direction: HorizontalFacing,
other_block: &Block,
other_block_state: &BlockState,
) -> bool {
let mut connected = other_block == block
|| (other_block_state.is_solid() && other_block_state.is_full_cube())
|| other_block_state.is_side_solid(BlockDirection::from_cardinal_direction(
direction.opposite(),
))
|| other_block.has_tag(&tag::Block::MINECRAFT_WALLS)
|| other_block == &Block::IRON_BARS
|| other_block.has_tag(&tag::Block::C_GLASS_PANES);
// fence gates do not pass is_side_solid check
if !connected && other_block.has_tag(&tag::Block::MINECRAFT_FENCE_GATES) {
let fence_props = FenceGateProperties::from_state_id(other_block_state.id);
if fence_props.facing == direction.rotate_clockwise()
|| fence_props.facing == direction.rotate_counter_clockwise()
{
connected = true;
}
}
connected
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum WallShape {
None,
Low,
Tall,
}
impl From<WallShape> for NorthWall {
fn from(value: WallShape) -> Self {
match value {
WallShape::None => Self::None,
WallShape::Low => Self::Low,
WallShape::Tall => Self::Tall,
}
}
}
impl From<WallShape> for SouthWall {
fn from(value: WallShape) -> Self {
match value {
WallShape::None => Self::None,
WallShape::Low => Self::Low,
WallShape::Tall => Self::Tall,
}
}
}
impl From<WallShape> for EastWall {
fn from(value: WallShape) -> Self {
match value {
WallShape::None => Self::None,
WallShape::Low => Self::Low,
WallShape::Tall => Self::Tall,
}
}
}
impl From<WallShape> for WestWall {
fn from(value: WallShape) -> Self {
match value {
WallShape::None => Self::None,
WallShape::Low => Self::Low,
WallShape::Tall => Self::Tall,
}
}
}