Skip to main content

steel_core/behavior/blocks/vegetation/
cave_vines_plant_block.rs

1use rand::Rng;
2use std::sync::Arc;
3use steel_macros::block_behavior;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::blocks::properties::{BlockStateProperties, BoolProperty};
6use steel_registry::item_stack::ItemStack;
7use steel_registry::items::item::BlockHitResult;
8use steel_registry::{vanilla_blocks, vanilla_items};
9use steel_utils::types::UpdateFlags;
10use steel_utils::{BlockPos, BlockStateId, Direction};
11
12use crate::behavior::blocks::CaveVinesBlock;
13use crate::behavior::blocks::vegetation::bonemealable::{BonemealAction, Bonemealable};
14use crate::behavior::blocks::vegetation::growing_plant_body_block::GrowingPlantBodyBlock;
15use crate::behavior::context::BlockPlaceContext;
16use crate::behavior::{InteractionResult, InventoryAccess};
17use crate::player::Player;
18use crate::world::{LevelReader, ScheduledTickAccess};
19use crate::{behavior::block::BlockBehavior, world::World};
20
21use super::BlockRef;
22
23/// Vanilla `CaveVinesPlantBlock` (body) survival.
24#[block_behavior]
25pub struct CaveVinesPlantBlock {
26    block: BlockRef,
27}
28
29const BERRIES: BoolProperty = BlockStateProperties::BERRIES;
30
31impl CaveVinesPlantBlock {
32    /// Creates a new cave vines plant (body) block behavior.
33    #[must_use]
34    pub const fn new(block: BlockRef) -> Self {
35        Self { block }
36    }
37
38    fn can_grow_into(state: BlockStateId) -> bool {
39        state.is_air()
40    }
41
42    const fn growing_plant_body_block(&self) -> GrowingPlantBodyBlock {
43        GrowingPlantBodyBlock::new(
44            self.block,
45            Direction::Down,
46            false,
47            &vanilla_blocks::CAVE_VINES,
48            Self::can_grow_into,
49        )
50        .with_update_head_after_converted_from_body(Self::update_head_after_converted_from_body)
51    }
52
53    fn update_head_after_converted_from_body(
54        body_state: BlockStateId,
55        head_state: BlockStateId,
56    ) -> BlockStateId {
57        head_state.set_value(&BERRIES, body_state.get_value(&BERRIES))
58    }
59}
60
61impl BlockBehavior for CaveVinesPlantBlock {
62    fn can_be_replaced(&self, state: BlockStateId, context: &BlockPlaceContext<'_>) -> bool {
63        self.growing_plant_body_block()
64            .can_be_replaced(state, context)
65    }
66
67    fn use_without_item(
68        &self,
69        state: BlockStateId,
70        world: &Arc<World>,
71        pos: BlockPos,
72        player: &Player,
73        _hit_result: &BlockHitResult,
74        _inv: &mut InventoryAccess,
75    ) -> InteractionResult {
76        CaveVinesBlock::use_block(player, state, world, pos)
77    }
78
79    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
80        Some(self)
81    }
82    fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
83        self.growing_plant_body_block()
84            .can_survive(state, world, pos)
85    }
86    fn update_shape(
87        &self,
88        state: BlockStateId,
89        world: &dyn ScheduledTickAccess,
90        pos: BlockPos,
91        direction: Direction,
92        neighbor_pos: BlockPos,
93        neighbor_state: BlockStateId,
94    ) -> BlockStateId {
95        self.growing_plant_body_block().update_shape(
96            state,
97            world,
98            pos,
99            direction,
100            neighbor_pos,
101            neighbor_state,
102        )
103    }
104    fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
105        self.growing_plant_body_block().tick(state, world, pos);
106    }
107
108    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
109        self.growing_plant_body_block()
110            .get_state_for_placement(context)
111    }
112
113    fn get_clone_item_stack(
114        &self,
115        _block: BlockRef,
116        _state: BlockStateId,
117        _include_data: bool,
118    ) -> Option<ItemStack> {
119        Some(ItemStack::new(&vanilla_items::GLOW_BERRIES))
120    }
121}
122impl Bonemealable for CaveVinesPlantBlock {
123    fn is_valid_bonemeal_target(
124        &self,
125        state: BlockStateId,
126        _world: &dyn LevelReader,
127        _pos: BlockPos,
128    ) -> bool {
129        !state.get_value(&BERRIES)
130    }
131
132    fn perform_bonemeal(
133        &self,
134        state: BlockStateId,
135        world: &Arc<World>,
136        _rng: &mut dyn Rng,
137        pos: BlockPos,
138    ) {
139        world.set_block(
140            pos,
141            state.set_value(&BERRIES, true),
142            UpdateFlags::UPDATE_CLIENTS,
143        );
144    }
145
146    fn bonemeal_action_type(&self) -> BonemealAction {
147        BonemealAction::Grower
148    }
149}
150
151#[cfg(test)]
152mod tests {
153    use steel_registry::init_vanilla_registry;
154
155    use super::*;
156    use crate::test_support::TestLevel;
157
158    #[test]
159    fn body_conversion_preserves_berries() {
160        init_vanilla_registry();
161
162        let behavior = CaveVinesPlantBlock::new(&vanilla_blocks::CAVE_VINES_PLANT);
163        let state = vanilla_blocks::CAVE_VINES_PLANT
164            .default_state()
165            .set_value(&BERRIES, true);
166        let level = TestLevel::default();
167
168        let converted = behavior.update_shape(
169            state,
170            &level,
171            BlockPos::ZERO,
172            Direction::Down,
173            BlockPos::ZERO.below(),
174            vanilla_blocks::AIR.default_state(),
175        );
176
177        assert_eq!(converted.get_block(), &vanilla_blocks::CAVE_VINES);
178        assert!(converted.get_value(&BERRIES));
179    }
180
181    #[test]
182    fn clone_item_is_glow_berries() {
183        init_vanilla_registry();
184
185        let behavior = CaveVinesPlantBlock::new(&vanilla_blocks::CAVE_VINES_PLANT);
186        let item = behavior
187            .get_clone_item_stack(
188                &vanilla_blocks::CAVE_VINES_PLANT,
189                vanilla_blocks::CAVE_VINES_PLANT.default_state(),
190                false,
191            )
192            .expect("cave vines plants have a vanilla clone item");
193
194        assert!(item.is(&vanilla_items::GLOW_BERRIES));
195    }
196}