steel_core/behavior/blocks/redstone/
lightning_rod_block.rs1use std::sync::Arc;
2
3use steel_macros::block_behavior;
4use steel_registry::blocks::BlockRef;
5use steel_registry::blocks::block_state_ext::BlockStateExt as _;
6use steel_registry::blocks::properties::{
7 BlockStateProperties, BoolProperty, Direction, EnumProperty,
8};
9use steel_registry::level_events;
10use steel_utils::types::UpdateFlags;
11use steel_utils::{BlockPos, BlockStateId};
12
13use crate::behavior::block::schedule_water_tick_if_waterlogged;
14use crate::behavior::blocks::redstone::{MAX_REDSTONE_SIGNAL, MIN_REDSTONE_SIGNAL};
15use crate::behavior::{BlockBehavior, BlockPlaceContext};
16use crate::entity::ai::path::PathComputationType;
17use crate::world::{LevelReader, ScheduledTickAccess, SignalQueryContext, World};
18
19#[block_behavior]
21pub struct LightningRodBlock {
22 block: BlockRef,
23}
24
25const WATERLOGGED: &BoolProperty = &BlockStateProperties::WATERLOGGED;
26const FACING: &EnumProperty<Direction> = &BlockStateProperties::FACING;
27const POWERED: &BoolProperty = &BlockStateProperties::POWERED;
28
29const ACTIVATION_TICKS: i32 = 8;
30
31impl LightningRodBlock {
32 #[must_use]
34 pub const fn new(block: BlockRef) -> Self {
35 Self { block }
36 }
37 fn update_neighbors(block: BlockRef, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
38 let front = state.get_value(FACING).opposite();
39 world.update_neighbors_at(pos.relative(front), block);
41 }
42 #[expect(
43 dead_code,
44 reason = "We need to call this function once on_lightning_strike gets implemented in BlockBehavior"
45 )]
46 fn on_lightning_strike(
47 block: BlockRef,
48 state: BlockStateId,
49 world: &Arc<World>,
50 pos: BlockPos,
51 ) {
52 world.set_block(pos, state.set_value(POWERED, true), UpdateFlags::UPDATE_ALL);
53 Self::update_neighbors(block, state, world, pos);
54 world.schedule_block_tick_default(pos, block, ACTIVATION_TICKS);
55 world.level_event(
56 level_events::PARTICLES_ELECTRIC_SPARK,
57 pos,
58 state.get_value(FACING).get_axis().ordinal(),
59 None,
60 );
61 }
62}
63
64impl BlockBehavior for LightningRodBlock {
65 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
66 Some(
67 self.block
68 .default_state()
69 .set_value(FACING, context.clicked_face())
70 .set_value(WATERLOGGED, context.is_water_source()),
71 )
72 }
73
74 fn update_shape(
75 &self,
76 state: BlockStateId,
77 world: &dyn ScheduledTickAccess,
78 pos: BlockPos,
79 _direction: Direction,
80 _neighbor_pos: BlockPos,
81 _neighbor_state: BlockStateId,
82 ) -> BlockStateId {
83 schedule_water_tick_if_waterlogged(state, world, pos);
84
85 state
86 }
87
88 fn get_own_signal(
89 &self,
90 state: BlockStateId,
91 _world: &dyn LevelReader,
92 _pos: BlockPos,
93 _context: SignalQueryContext,
94 ) -> i32 {
95 if state.get_value(POWERED) {
96 return MAX_REDSTONE_SIGNAL;
97 }
98 MIN_REDSTONE_SIGNAL
99 }
100
101 fn get_direct_signal(
102 &self,
103 state: BlockStateId,
104 _world: &dyn LevelReader,
105 _pos: BlockPos,
106 direction: Direction,
107 _context: SignalQueryContext,
108 ) -> i32 {
109 if state.get_value(POWERED) && state.get_value(FACING) == direction {
110 return MAX_REDSTONE_SIGNAL;
111 }
112 MIN_REDSTONE_SIGNAL
113 }
114
115 fn tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
116 world.set_block(
117 pos,
118 state.set_value(POWERED, false),
119 UpdateFlags::UPDATE_ALL,
120 );
121 Self::update_neighbors(self.block, state, world, pos);
122 }
123
124 fn affect_neighbors_after_removal(
125 &self,
126 state: BlockStateId,
127 world: &Arc<World>,
128 pos: BlockPos,
129 _moved_by_piston: bool,
130 ) {
131 if state.get_value(POWERED) {
132 Self::update_neighbors(self.block, state, world, pos);
133 }
134 }
135
136 fn on_place(
137 &self,
138 state: BlockStateId,
139 world: &Arc<World>,
140 pos: BlockPos,
141 old_state: BlockStateId,
142 _moved_by_piston: bool,
143 ) {
144 if state.get_block() != old_state.get_block()
145 && state.get_value(POWERED)
146 && !world.has_scheduled_block_tick(pos, self.block)
147 {
148 world.schedule_block_tick_default(pos, self.block, ACTIVATION_TICKS);
149 }
150 }
151
152 fn is_signal_source(&self, _state: BlockStateId, _context: SignalQueryContext) -> bool {
153 true
154 }
155
156 fn is_pathfindable(
157 &self,
158 _state: BlockStateId,
159 _computation_type: PathComputationType,
160 ) -> bool {
161 false
162 }
163
164 }