Skip to main content

steel_core/behavior/blocks/decoration/
lantern_block.rs

1use steel_macros::block_behavior;
2use steel_registry::{
3    blocks::{
4        BlockRef,
5        block_state_ext::BlockStateExt,
6        properties::{BlockStateProperties, BoolProperty},
7        shapes::SupportType,
8    },
9    vanilla_blocks,
10};
11use steel_utils::{BlockPos, BlockStateId, Direction, axis::Axis};
12
13use crate::{
14    behavior::{BlockBehavior, BlockPlaceContext, block::schedule_water_tick_if_waterlogged},
15    entity::ai::path::PathComputationType,
16    world::{LevelReader, ScheduledTickAccess},
17};
18
19/// Behavior for all non-weathering Lantern type blocks
20#[block_behavior]
21pub struct LanternBlock {
22    block: BlockRef,
23}
24
25const HANGING: &BoolProperty = &BlockStateProperties::HANGING;
26const WATERLOGGED: &BoolProperty = &BlockStateProperties::WATERLOGGED;
27
28impl LanternBlock {
29    /// Creates a new lantern block behavior for the given block
30    #[must_use]
31    pub const fn new(block: BlockRef) -> Self {
32        Self { block }
33    }
34
35    fn get_connected_dir(state: BlockStateId) -> Direction {
36        if state.get_value(HANGING) {
37            return Direction::Down;
38        }
39        Direction::Up
40    }
41}
42
43impl BlockBehavior for LanternBlock {
44    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
45        for dir in context.get_nearest_looking_directions() {
46            if dir.axis() == Axis::Y {
47                let state = self
48                    .block
49                    .default_state()
50                    .set_value(HANGING, dir == Direction::Up);
51                if self.can_survive(state, context.world, context.place_pos()) {
52                    return Some(state.set_value(WATERLOGGED, context.is_water_source()));
53                }
54            }
55        }
56        None
57    }
58
59    fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
60        let dir = Self::get_connected_dir(state).opposite();
61        let relative_pos = pos.relative(dir);
62        world.is_face_sturdy_for(
63            world.get_block_state(relative_pos),
64            relative_pos,
65            dir.opposite(),
66            SupportType::Center,
67        )
68    }
69
70    fn update_shape(
71        &self,
72        state: BlockStateId,
73        world: &dyn ScheduledTickAccess,
74        pos: BlockPos,
75        direction: Direction,
76        _neighbor_pos: BlockPos,
77        _neighbor_state: BlockStateId,
78    ) -> BlockStateId {
79        schedule_water_tick_if_waterlogged(state, world, pos);
80
81        if Self::get_connected_dir(state).opposite() == direction
82            && !self.can_survive(state, world, pos)
83        {
84            return vanilla_blocks::AIR.default_state();
85        }
86        state
87    }
88
89    fn is_pathfindable(
90        &self,
91        _state: BlockStateId,
92        _computation_type: PathComputationType,
93    ) -> bool {
94        false
95    }
96}