Skip to main content

steel_core/behavior/blocks/building/
ice_block.rs

1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::BlockRef;
5use steel_registry::blocks::block_state_ext::BlockStateExt;
6use steel_registry::item_stack::ItemStack;
7use steel_registry::vanilla_enchantment_tags::EnchantmentTag;
8use steel_registry::{REGISTRY, RegistryExt, TaggedRegistryExt, vanilla_blocks};
9use steel_utils::types::UpdateFlags;
10use steel_utils::{BlockPos, BlockStateId};
11
12use crate::behavior::{BlockBehavior, BlockPlaceContext};
13use crate::block_entity::SharedBlockEntity;
14use crate::chunk::light::LightLayer;
15use crate::player::Player;
16use crate::world::World;
17
18/// Vanilla `IceBlock` behavior.
19#[block_behavior]
20pub struct IceBlock {
21    block: BlockRef,
22}
23
24pub const BASE_MELT_LIGHT_LEVEL: u8 = 11;
25
26impl IceBlock {
27    /// Creates an ice block behavior.
28    #[must_use]
29    pub const fn new(block: BlockRef) -> Self {
30        Self { block }
31    }
32
33    /// Vanilla `IceBlock.meltsInto`.
34    #[must_use]
35    pub fn melts_into() -> BlockStateId {
36        vanilla_blocks::WATER.default_state()
37    }
38
39    /// Vanilla `IceBlock.melt`.
40    pub fn melt(_state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
41        if world.dimension_type.water_evaporates {
42            world.set_block(
43                pos,
44                vanilla_blocks::AIR.default_state(),
45                UpdateFlags::UPDATE_ALL,
46            );
47        } else {
48            world.set_block(pos, Self::melts_into(), UpdateFlags::UPDATE_ALL);
49            world.update_neighbors_at(pos, Self::melts_into().get_block());
50        }
51    }
52
53    /// Checks if tool prevents ice from melting (e.g. Silk Touch / `PREVENTS_ICE_MELTING` tag).
54    #[must_use]
55    pub fn prevents_ice_melting(tool: &ItemStack) -> bool {
56        let Some(enchantments) = tool.get_enchantments() else {
57            return false;
58        };
59        enchantments.iter().any(|(key, _)| {
60            REGISTRY.enchantments.by_key(key).is_some_and(|ench| {
61                REGISTRY
62                    .enchantments
63                    .is_in_tag(ench, &EnchantmentTag::PREVENTS_ICE_MELTING)
64            })
65        })
66    }
67}
68
69impl BlockBehavior for IceBlock {
70    fn get_state_for_placement(&self, _context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
71        Some(self.block.default_state())
72    }
73
74    fn player_destroy(
75        &self,
76        world: &Arc<World>,
77        _player: &Player,
78        pos: BlockPos,
79        _state: BlockStateId,
80        _block_entity: Option<&SharedBlockEntity>,
81        tool: &ItemStack,
82    ) {
83        if !Self::prevents_ice_melting(tool) {
84            if world.dimension_type.water_evaporates {
85                world.set_block(
86                    pos,
87                    vanilla_blocks::AIR.default_state(),
88                    UpdateFlags::UPDATE_ALL,
89                );
90                return;
91            }
92
93            let below_state = world.get_block_state(pos.below());
94            if below_state.blocks_motion()
95                || !below_state.get_fluid_state().is_empty()
96                || below_state.get_block().config.liquid
97            {
98                world.set_block(pos, Self::melts_into(), UpdateFlags::UPDATE_ALL);
99            }
100        }
101    }
102    fn random_tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
103        if world.light_value_at(LightLayer::Block, pos)
104            > BASE_MELT_LIGHT_LEVEL - state.get_light_dampening()
105        {
106            Self::melt(state, world, pos);
107        }
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::IceBlock;
114    use steel_registry::init_vanilla_registry;
115    use steel_registry::item_stack::ItemStack;
116    use steel_registry::{vanilla_blocks, vanilla_enchantments, vanilla_items};
117
118    #[test]
119    fn melts_into_water_by_default() {
120        init_vanilla_registry();
121        assert_eq!(
122            IceBlock::melts_into(),
123            vanilla_blocks::WATER.default_state()
124        );
125    }
126
127    #[test]
128    fn prevents_ice_melting_with_silk_touch() {
129        init_vanilla_registry();
130        let mut tool = ItemStack::new(&vanilla_items::DIAMOND_PICKAXE);
131        assert!(!IceBlock::prevents_ice_melting(&tool));
132
133        tool.upgrade_enchantment(vanilla_enchantments::SILK_TOUCH.key.clone(), 1);
134        assert!(IceBlock::prevents_ice_melting(&tool));
135    }
136}