steel_core/behavior/blocks/vegetation/
weeping_vines_block.rs1use rand::Rng;
2use std::sync::Arc;
3use steel_macros::block_behavior;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::vanilla_blocks;
6use steel_utils::{BlockPos, BlockStateId, Direction};
7
8use crate::behavior::blocks::vegetation::bonemealable::{BonemealAction, Bonemealable};
9use crate::behavior::blocks::vegetation::growing_plant_head_block::{
10 GrowingPlantHeadBehavior, GrowingPlantHeadBlock,
11};
12use crate::behavior::blocks::vegetation::nether_vines;
13use crate::behavior::context::BlockPlaceContext;
14use crate::world::{LevelReader, World};
15use crate::{behavior::block::BlockBehavior, world::ScheduledTickAccess};
16
17use super::BlockRef;
18
19#[block_behavior]
21pub struct WeepingVinesBlock {
22 base: GrowingPlantHeadBlock,
23}
24
25impl WeepingVinesBlock {
26 #[must_use]
28 pub const fn new(block: BlockRef) -> Self {
29 Self {
30 base: GrowingPlantHeadBlock::new(
31 block,
32 Direction::Down,
33 false,
34 0.1,
35 &vanilla_blocks::WEEPING_VINES_PLANT,
36 Some(nether_vines::get_blocks_to_grow_when_bonemealed),
37 Self::can_grow_into,
38 ),
39 }
40 }
41
42 fn can_grow_into(state: BlockStateId) -> bool {
43 state.is_air()
44 }
45}
46
47impl BlockBehavior for WeepingVinesBlock {
48 fn can_survive(&self, state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
49 self.base.can_survive(state, world, pos)
50 }
51
52 fn random_tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
53 self.base.random_tick(state, world, pos);
54 }
55
56 fn update_shape(
57 &self,
58 state: BlockStateId,
59 world: &dyn ScheduledTickAccess,
60 pos: BlockPos,
61 direction: Direction,
62 neighbor_pos: BlockPos,
63 neighbor_state: BlockStateId,
64 ) -> BlockStateId {
65 self.base
66 .update_shape(state, world, pos, direction, neighbor_pos, neighbor_state)
67 }
68
69 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
70 self.base.tick(state, world, pos);
71 }
72
73 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
74 self.base.get_state_for_placement(context)
75 }
76
77 fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
78 Some(self)
79 }
80
81 fn as_growing_plant_head(&self) -> Option<&dyn GrowingPlantHeadBehavior> {
82 Some(&self.base)
83 }
84}
85impl Bonemealable for WeepingVinesBlock {
86 fn is_valid_bonemeal_target(
87 &self,
88 state: BlockStateId,
89 world: &dyn LevelReader,
90 pos: BlockPos,
91 ) -> bool {
92 self.base.is_valid_bonemeal_target(state, world, pos)
93 }
94
95 fn perform_bonemeal(
96 &self,
97 state: BlockStateId,
98 world: &Arc<World>,
99 rng: &mut dyn Rng,
100 pos: BlockPos,
101 ) {
102 self.base.perform_bonemeal(state, world, rng, pos);
103 }
104
105 fn bonemeal_action_type(&self) -> BonemealAction {
106 BonemealAction::Grower
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use steel_registry::init_vanilla_registry;
113
114 use super::*;
115 use crate::test_support::TestLevel;
116
117 #[test]
118 fn bonemeal_target_requires_open_growth_position() {
119 init_vanilla_registry();
120
121 let behavior = WeepingVinesBlock::new(&vanilla_blocks::WEEPING_VINES);
122 let state = vanilla_blocks::WEEPING_VINES.default_state();
123 let open_level = TestLevel::default();
124 assert!(behavior.is_valid_bonemeal_target(state, &open_level, BlockPos::ZERO));
125
126 let blocked_level = TestLevel::default().with_block(
127 BlockPos::ZERO.below(),
128 vanilla_blocks::NETHERRACK.default_state(),
129 );
130 assert!(!behavior.is_valid_bonemeal_target(state, &blocked_level, BlockPos::ZERO));
131 }
132
133 #[test]
134 fn connected_head_converts_to_body() {
135 init_vanilla_registry();
136
137 let behavior = WeepingVinesBlock::new(&vanilla_blocks::WEEPING_VINES);
138 let state = vanilla_blocks::WEEPING_VINES.default_state();
139 let level = TestLevel::default();
140 let converted = behavior.update_shape(
141 state,
142 &level,
143 BlockPos::ZERO,
144 Direction::Down,
145 BlockPos::ZERO.below(),
146 vanilla_blocks::WEEPING_VINES_PLANT.default_state(),
147 );
148
149 assert_eq!(
150 converted,
151 vanilla_blocks::WEEPING_VINES_PLANT.default_state()
152 );
153 }
154}