steel_core/behavior/blocks/falling/
falling_block.rs1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::properties::Direction;
5use steel_registry::blocks::{BlockRef, block_state_ext::BlockStateExt as _};
6use steel_registry::vanilla_block_tags::BlockTag;
7use steel_utils::{BlockPos, BlockStateId};
8
9use crate::behavior::{BlockBehavior, BlockPlaceContext, Fallable};
10use crate::entity::entities::FallingBlockEntity;
11use crate::world::{ScheduledTickAccess, World};
12
13const FALL_DELAY: i32 = 2;
14
15#[block_behavior(class = "ColoredFallingBlock")]
20pub struct FallingBlock {
21 block: BlockRef,
22}
23
24impl FallingBlock {
25 #[must_use]
27 pub const fn new(block: BlockRef) -> Self {
28 Self { block }
29 }
30
31 #[must_use]
33 pub const fn block(&self) -> BlockRef {
34 self.block
35 }
36
37 pub fn on_place(&self, world: &Arc<World>, pos: BlockPos) {
39 let _ = world.schedule_block_tick_default(pos, self.block, FALL_DELAY);
40 }
41
42 #[must_use]
44 pub fn update_shape(
45 &self,
46 state: BlockStateId,
47 ticks: &dyn ScheduledTickAccess,
48 pos: BlockPos,
49 ) -> BlockStateId {
50 let _ = ticks.schedule_block_tick_default(pos, self.block, FALL_DELAY);
51 state
52 }
53
54 #[must_use]
56 pub fn tick(
57 state: BlockStateId,
58 world: &Arc<World>,
59 pos: BlockPos,
60 ) -> Option<Arc<FallingBlockEntity>> {
61 if pos.y() < world.get_min_y() || !Self::is_free(world.get_block_state(pos.below())) {
62 return None;
63 }
64
65 Some(FallingBlockEntity::fall(world, pos, state))
66 }
67
68 #[must_use]
70 pub fn is_free(state: BlockStateId) -> bool {
71 let block = state.get_block();
72 state.is_air()
73 || block.has_tag(&BlockTag::FIRE)
74 || block.config.liquid
75 || state.is_replaceable()
76 }
77}
78
79impl Fallable for FallingBlock {}
80
81impl BlockBehavior for FallingBlock {
82 fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
83 Some(self.block.default_state())
84 }
85
86 fn on_place(
87 &self,
88 _state: BlockStateId,
89 world: &Arc<World>,
90 pos: BlockPos,
91 _old_state: BlockStateId,
92 _moved_by_piston: bool,
93 ) {
94 self.on_place(world, pos);
95 }
96
97 fn update_shape(
98 &self,
99 state: BlockStateId,
100 ticks: &dyn ScheduledTickAccess,
101 pos: BlockPos,
102 _direction: Direction,
103 _neighbor_pos: BlockPos,
104 _neighbor_state: BlockStateId,
105 ) -> BlockStateId {
106 self.update_shape(state, ticks, pos)
107 }
108
109 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
110 let _ = Self::tick(state, world, pos);
111 }
112
113 fn as_fallable(&self) -> Option<&dyn Fallable> {
114 Some(self)
115 }
116}