steel_core/behavior/blocks/building/
budding_amethyst.rs1use crate::{
2 behavior::{BlockBehavior, BlockPlaceContext, blocks::AmethystBlock},
3 entity::projectile::Projectile,
4 fluid::FluidStateExt as _,
5 world::{ClipHitResult, World},
6};
7use std::sync::Arc;
8use steel_macros::block_behavior;
9use steel_registry::{
10 REGISTRY, RegistryEntry,
11 blocks::{
12 BlockRef,
13 block_state_ext::BlockStateExt,
14 properties::{BlockStateProperties, BoolProperty, EnumProperty},
15 },
16 vanilla_blocks,
17};
18use steel_utils::{BlockPos, BlockStateId, Direction, types::UpdateFlags};
19
20#[block_behavior]
22pub struct BuddingAmethystBlock {
23 block: BlockRef,
24}
25
26const FACING: &EnumProperty<Direction> = &BlockStateProperties::FACING;
27const WATERLOGGED: &BoolProperty = &BlockStateProperties::WATERLOGGED;
28
29impl BuddingAmethystBlock {
30 #[must_use]
32 pub const fn new(block: BlockRef) -> Self {
33 Self { block }
34 }
35
36 fn can_cluster_grow_at_state(state: BlockStateId, block_id: usize) -> bool {
37 state.is_air()
38 || (block_id == vanilla_blocks::WATER.id() && state.get_fluid_state().is_full())
39 }
40
41 fn check_cluster(
42 state: BlockStateId,
43 block_id: usize,
44 direction: Direction,
45 block: BlockRef,
46 ) -> bool {
47 block_id == block.id() && state.get_value(FACING) == direction
48 }
49
50 fn growth_state(
51 block: BlockRef,
52 replaced_state: BlockStateId,
53 direction: Direction,
54 ) -> BlockStateId {
55 block
56 .default_state()
57 .set_value(FACING, direction)
58 .set_value(WATERLOGGED, replaced_state.get_fluid_state().is_water())
59 }
60}
61
62impl BlockBehavior for BuddingAmethystBlock {
63 fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
64 Some(self.block.default_state())
65 }
66
67 fn random_tick(&self, _state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
68 if rand::random_range(0..5) == 0 {
69 let direction = Direction::random();
70 let grow_pos = pos.relative(direction);
71 let state = world.get_block_state(grow_pos);
72 let Some(&block_id) = REGISTRY.blocks.state_to_block_id.get(state.0 as usize) else {
73 panic!(
74 "budding amethyst received invalid block state id {}",
75 state.0
76 );
77 };
78
79 let mut growth_stage: Option<BlockRef> = None;
80 if Self::can_cluster_grow_at_state(state, block_id) {
81 growth_stage = Some(&vanilla_blocks::SMALL_AMETHYST_BUD);
82 } else if Self::check_cluster(
83 state,
84 block_id,
85 direction,
86 &vanilla_blocks::SMALL_AMETHYST_BUD,
87 ) {
88 growth_stage = Some(&vanilla_blocks::MEDIUM_AMETHYST_BUD);
89 } else if Self::check_cluster(
90 state,
91 block_id,
92 direction,
93 &vanilla_blocks::MEDIUM_AMETHYST_BUD,
94 ) {
95 growth_stage = Some(&vanilla_blocks::LARGE_AMETHYST_BUD);
96 } else if Self::check_cluster(
97 state,
98 block_id,
99 direction,
100 &vanilla_blocks::LARGE_AMETHYST_BUD,
101 ) {
102 growth_stage = Some(&vanilla_blocks::AMETHYST_CLUSTER);
103 }
104
105 if let Some(growth_stage) = growth_stage {
106 let block_state = Self::growth_state(growth_stage, state, direction);
107 world.set_block(grow_pos, block_state, UpdateFlags::UPDATE_ALL);
108 }
109 }
110 }
111 fn on_projectile_hit(
112 &self,
113 _state: BlockStateId,
114 world: &Arc<World>,
115 hit: &ClipHitResult,
116 _projectile: &dyn Projectile,
117 ) {
118 AmethystBlock::play_projectile_hit_sound(world, hit.block_pos);
119 }
120}
121
122#[cfg(test)]
123mod tests {
124 use super::*;
125 use crate::behavior::init_behaviors;
126 use steel_registry::{blocks::properties::BlockStateProperties, init_vanilla_registry};
127
128 #[test]
129 fn growth_state_waterlogs_when_replacing_water_block() {
130 init_vanilla_registry();
131 init_behaviors();
132
133 let state = BuddingAmethystBlock::growth_state(
134 &vanilla_blocks::SMALL_AMETHYST_BUD,
135 vanilla_blocks::WATER.default_state(),
136 Direction::Up,
137 );
138
139 assert!(state.get_value(WATERLOGGED));
140 }
141
142 #[test]
143 fn cluster_can_grow_in_falling_full_water() {
144 init_vanilla_registry();
145 init_behaviors();
146
147 let falling_full_water = vanilla_blocks::WATER
148 .default_state()
149 .set_value(&BlockStateProperties::LEVEL, 8);
150
151 assert!(BuddingAmethystBlock::can_cluster_grow_at_state(
152 falling_full_water,
153 vanilla_blocks::WATER.id()
154 ));
155 }
156
157 #[test]
158 fn cluster_cannot_grow_in_partial_flowing_water() {
159 init_vanilla_registry();
160 init_behaviors();
161
162 let partial_flowing_water = vanilla_blocks::WATER
163 .default_state()
164 .set_value(&BlockStateProperties::LEVEL, 1);
165
166 assert!(!BuddingAmethystBlock::can_cluster_grow_at_state(
167 partial_flowing_water,
168 vanilla_blocks::WATER.id()
169 ));
170 }
171}