steel_core/behavior/blocks/vegetation/
chorus_flower_block.rs1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::block_state_ext::BlockStateExt;
5use steel_registry::vanilla_block_tags::BlockTag;
6use steel_utils::{BlockPos, BlockStateId, Direction};
7
8use crate::behavior::block::BlockBehavior;
9use crate::behavior::context::BlockPlaceContext;
10use crate::entity::projectile::Projectile;
11use crate::world::{ClipHitResult, LevelReader, World};
12
13use super::{BlockRef, default_surviving_state};
14
15const HORIZONTAL_DIRECTIONS: [Direction; 4] = [
16 Direction::North,
17 Direction::East,
18 Direction::South,
19 Direction::West,
20];
21
22#[block_behavior]
25pub struct ChorusFlowerBlock {
26 block: BlockRef,
27 #[json_arg(vanilla_blocks)]
28 plant: BlockRef,
29}
30
31impl ChorusFlowerBlock {
32 #[must_use]
34 pub const fn new(block: BlockRef, plant: BlockRef) -> Self {
35 Self { block, plant }
36 }
37
38 fn projectile_can_break(projectile: &dyn Projectile, world: &World, pos: BlockPos) -> bool {
39 projectile.projectile_may_interact(world, pos) && projectile.may_break(world)
40 }
41}
42
43impl BlockBehavior for ChorusFlowerBlock {
44 fn can_survive(&self, _state: BlockStateId, world: &dyn LevelReader, pos: BlockPos) -> bool {
45 let below_state = world.get_block_state(pos.below());
46 if below_state.get_block() == self.plant
47 || below_state
48 .get_block()
49 .has_tag(&BlockTag::SUPPORTS_CHORUS_FLOWER)
50 {
51 return true;
52 }
53
54 if !below_state.is_air() {
55 return false;
56 }
57
58 let mut has_single_plant_neighbor = false;
59 for direction in HORIZONTAL_DIRECTIONS {
60 let neighbor_state = world.get_block_state(pos.relative(direction));
61 if neighbor_state.get_block() == self.plant {
62 if has_single_plant_neighbor {
63 return false;
64 }
65 has_single_plant_neighbor = true;
66 } else if !neighbor_state.is_air() {
67 return false;
68 }
69 }
70
71 has_single_plant_neighbor
72 }
73
74 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
75 default_surviving_state(self.block, self, context)
76 }
77
78 fn on_projectile_hit(
79 &self,
80 _state: BlockStateId,
81 world: &Arc<World>,
82 hit: &ClipHitResult,
83 projectile: &dyn Projectile,
84 ) {
85 if Self::projectile_can_break(projectile, world, hit.block_pos) {
86 world.destroy_block_by_entity(hit.block_pos, true, projectile.as_entity_event_source());
87 }
88 }
89}