Skip to main content

steel_core/behavior/blocks/building/
netherrack_block.rs

1//! `NetherrackBlock` behavior (`net.minecraft.world.level.block.NetherrackBlock`).
2
3use std::sync::Arc;
4
5use rand::RngExt;
6use steel_macros::block_behavior;
7use steel_registry::blocks::BlockRef;
8use steel_registry::blocks::block_state_ext::BlockStateExt as _;
9use steel_registry::vanilla_block_tags::BlockTag;
10use steel_registry::vanilla_blocks;
11use steel_utils::{BlockPos, BlockStateId, types::UpdateFlags};
12
13use crate::behavior::blocks::vegetation::bonemealable::{BonemealAction, Bonemealable};
14use crate::behavior::{BlockBehavior, BlockPlaceContext};
15use crate::world::{LevelReader, World};
16
17/// Chance to pick `Warped Nylium` when both nylium types are nearby.
18const WARPED_VS_CRIMSON_CHANCE: f64 = 0.5;
19
20/// Vanilla `NetherrackBlock`.
21#[block_behavior]
22pub struct NetherrackBlock {
23    block: BlockRef,
24}
25
26impl NetherrackBlock {
27    /// Creates the behavior.
28    #[must_use]
29    pub const fn new(block: BlockRef) -> Self {
30        Self { block }
31    }
32}
33
34impl BlockBehavior for NetherrackBlock {
35    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
36        Some(self.block.default_state())
37    }
38
39    fn as_bonemealable(&self) -> Option<&dyn Bonemealable> {
40        Some(self)
41    }
42}
43
44impl Bonemealable for NetherrackBlock {
45    fn is_valid_bonemeal_target(
46        &self,
47        _state: BlockStateId,
48        world: &dyn LevelReader,
49        pos: BlockPos,
50    ) -> bool {
51        if world.get_block_state(pos.above()).get_light_dampening() != 0 {
52            return false;
53        }
54        for check_pos in BlockPos::between_closed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1)) {
55            if world
56                .get_block_state(check_pos)
57                .get_block()
58                .has_tag(&BlockTag::NYLIUM)
59            {
60                return true;
61            }
62        }
63        false
64    }
65
66    fn perform_bonemeal(
67        &self,
68        _state: BlockStateId,
69        world: &Arc<World>,
70        rng: &mut dyn rand::Rng,
71        pos: BlockPos,
72    ) {
73        let mut found_warped = false;
74        let mut found_crimson = false;
75        for check_pos in BlockPos::between_closed(pos.offset(-1, -1, -1), pos.offset(1, 1, 1)) {
76            let state = world.get_block_state(check_pos);
77            let block = state.get_block();
78            if block == &vanilla_blocks::WARPED_NYLIUM {
79                found_warped = true;
80            }
81            if block == &vanilla_blocks::CRIMSON_NYLIUM {
82                found_crimson = true;
83            }
84            if found_warped && found_crimson {
85                break;
86            }
87        }
88        let new_state = if found_warped && found_crimson {
89            if rng.random_bool(WARPED_VS_CRIMSON_CHANCE) {
90                vanilla_blocks::WARPED_NYLIUM.default_state()
91            } else {
92                vanilla_blocks::CRIMSON_NYLIUM.default_state()
93            }
94        } else if found_warped {
95            vanilla_blocks::WARPED_NYLIUM.default_state()
96        } else if found_crimson {
97            vanilla_blocks::CRIMSON_NYLIUM.default_state()
98        } else {
99            return;
100        };
101        world.set_block(pos, new_state, UpdateFlags::UPDATE_ALL);
102    }
103
104    fn bonemeal_action_type(&self) -> BonemealAction {
105        BonemealAction::NeighborSpreader
106    }
107}