Skip to main content

steel_core/behavior/blocks/falling/
dragon_egg_block.rs

1use std::sync::Arc;
2
3use rand::random_range;
4use steel_macros::block_behavior;
5use steel_registry::blocks::BlockRef;
6use steel_registry::blocks::block_state_ext::BlockStateExt;
7use steel_registry::blocks::properties::Direction;
8use steel_utils::types::UpdateFlags;
9use steel_utils::{BlockPos, BlockStateId};
10
11use crate::behavior::{BlockBehavior, BlockPlaceContext, InteractionResult, InventoryAccess};
12use crate::entity::ai::path::PathComputationType;
13use crate::player::Player;
14use crate::world::{LevelAccessor, ScheduledTickAccess, World};
15use steel_registry::items::item::BlockHitResult;
16
17use super::FallingBlock;
18
19const DRAGON_EGG_FALL_DELAY: i32 = 5;
20const DRAGON_EGG_MAX_TELEPORTS: u32 = 1000;
21const DRAGON_EGG_MAX_X: i32 = 16;
22const DRAGON_EGG_MAX_Y: i32 = 8;
23const DRAGON_EGG_MAX_Z: i32 = 16;
24
25/// Vanilla `Dragon Egg` behavior.
26#[block_behavior]
27pub struct DragonEggBlock {
28    falling: FallingBlock,
29}
30
31impl DragonEggBlock {
32    /// Creates the server-side behavior for the dragon egg
33    #[must_use]
34    pub const fn new(block: BlockRef) -> Self {
35        Self {
36            falling: FallingBlock::new(block),
37        }
38    }
39
40    fn teleport(state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
41        for _ in 0..DRAGON_EGG_MAX_TELEPORTS {
42            let x = random_range(0..DRAGON_EGG_MAX_X) - random_range(0..DRAGON_EGG_MAX_X);
43            let y = random_range(0..DRAGON_EGG_MAX_Y) - random_range(0..DRAGON_EGG_MAX_Y);
44            let z = random_range(0..DRAGON_EGG_MAX_Z) - random_range(0..DRAGON_EGG_MAX_Z);
45
46            let new_pos = BlockPos::new(pos.x() + x, pos.y() + y, pos.z() + z);
47
48            if !world.get_block_state(new_pos).is_air() {
49                continue;
50            }
51
52            if world.get_block_state(new_pos.below()).is_air() {
53                continue;
54            }
55
56            if !world.is_block_within_world_border(new_pos) {
57                continue;
58            }
59
60            if world.is_outside_build_height(new_pos.y()) {
61                continue;
62            }
63
64            world.set_block_state(new_pos, state, UpdateFlags::UPDATE_CLIENTS);
65            world.remove_block(pos, false);
66            break;
67        }
68    }
69}
70
71impl BlockBehavior for DragonEggBlock {
72    fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
73        self.falling.get_state_for_placement(context)
74    }
75
76    fn on_place(
77        &self,
78        _state: BlockStateId,
79        world: &Arc<World>,
80        pos: BlockPos,
81        _old_state: BlockStateId,
82        _moved_by_piston: bool,
83    ) {
84        let _ = world.schedule_block_tick_default(pos, self.falling.block(), DRAGON_EGG_FALL_DELAY);
85    }
86
87    fn update_shape(
88        &self,
89        state: BlockStateId,
90        ticks: &dyn ScheduledTickAccess,
91        pos: BlockPos,
92        _direction: Direction,
93        _neighbor_pos: BlockPos,
94        _neighbor_state: BlockStateId,
95    ) -> BlockStateId {
96        let _ = ticks.schedule_block_tick_default(pos, self.falling.block(), DRAGON_EGG_FALL_DELAY);
97        state
98    }
99
100    fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
101        BlockBehavior::tick(&self.falling, state, world, pos);
102    }
103
104    fn use_without_item(
105        &self,
106        state: BlockStateId,
107        world: &Arc<World>,
108        pos: BlockPos,
109        _player: &Player,
110        _hit_result: &BlockHitResult,
111        _inv: &mut InventoryAccess,
112    ) -> InteractionResult {
113        Self::teleport(state, world, pos);
114        InteractionResult::Success
115    }
116
117    fn attack(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos, _player: &Player) {
118        Self::teleport(state, world, pos);
119    }
120
121    fn is_pathfindable(
122        &self,
123        _state: BlockStateId,
124        _computation_type: PathComputationType,
125    ) -> bool {
126        false
127    }
128}