steel_core/behavior/blocks/building/
bar_block.rs1use std::sync::Arc;
6use steel_macros::block_behavior;
7use steel_registry::blocks::BlockRef;
8use steel_registry::blocks::block_state_ext::BlockStateExt;
9use steel_registry::blocks::properties::{BlockStateProperties, BoolProperty, Direction};
10use steel_registry::vanilla_block_tags::BlockTag;
11use steel_utils::{BlockPos, BlockStateId};
12
13use crate::behavior::block::{BlockBehavior, schedule_water_tick_if_waterlogged};
14use crate::behavior::blocks::WeatherState;
15use crate::behavior::blocks::building::WeatheringCopper;
16use crate::behavior::blocks::utils::is_excluded_for_connection;
17use crate::behavior::context::BlockPlaceContext;
18use crate::entity::ai::path::PathComputationType;
19use crate::world::{LevelReader, ScheduledTickAccess, World};
20
21#[block_behavior]
29pub struct IronBarsBlock {
30 block: BlockRef,
31}
32
33const NORTH: BoolProperty = BlockStateProperties::NORTH;
35const EAST: BoolProperty = BlockStateProperties::EAST;
37const SOUTH: BoolProperty = BlockStateProperties::SOUTH;
39const WEST: BoolProperty = BlockStateProperties::WEST;
41const WATERLOGGED: BoolProperty = BlockStateProperties::WATERLOGGED;
43
44impl IronBarsBlock {
45 #[must_use]
47 pub const fn new(block: BlockRef) -> Self {
48 Self { block }
49 }
50}
51
52impl BlockBehavior for IronBarsBlock {
53 fn update_shape(
54 &self,
55 state: BlockStateId,
56 world: &dyn ScheduledTickAccess,
57 pos: BlockPos,
58 direction: Direction,
59 neighbor_pos: BlockPos,
60 neighbor_state: BlockStateId,
61 ) -> BlockStateId {
62 schedule_water_tick_if_waterlogged(state, world, pos);
63 update_shape(world, state, neighbor_state, neighbor_pos, direction)
64 }
65
66 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
67 Some(
68 get_connection_state(self.block, context.world, &context.place_pos())
69 .set_value(&WATERLOGGED, context.is_water_source()),
70 )
71 }
72
73 fn is_pathfindable(
74 &self,
75 _state: BlockStateId,
76 _computation_type: PathComputationType,
77 ) -> bool {
78 false
79 }
80}
81
82#[block_behavior]
90pub struct WeatheringCopperBarsBlock {
91 block: BlockRef,
92 #[json_arg(r#enum = "WeatherState", json = "weather_state")]
93 weathering: WeatheringCopper,
94}
95
96impl WeatheringCopperBarsBlock {
97 #[must_use]
99 pub const fn new(block: BlockRef, weather_state: WeatherState) -> Self {
100 Self {
101 block,
102 weathering: WeatheringCopper::new(weather_state),
103 }
104 }
105}
106
107impl BlockBehavior for WeatheringCopperBarsBlock {
108 fn update_shape(
109 &self,
110 state: BlockStateId,
111 world: &dyn ScheduledTickAccess,
112 pos: BlockPos,
113 direction: Direction,
114 neighbor_pos: BlockPos,
115 neighbor_state: BlockStateId,
116 ) -> BlockStateId {
117 schedule_water_tick_if_waterlogged(state, world, pos);
118 update_shape(world, state, neighbor_state, neighbor_pos, direction)
119 }
120
121 fn get_state_for_placement(&self, context: &BlockPlaceContext<'_>) -> Option<BlockStateId> {
122 Some(
123 get_connection_state(self.block, context.world, &context.place_pos())
124 .set_value(&WATERLOGGED, context.is_water_source()),
125 )
126 }
127
128 fn random_tick(&self, state: BlockStateId, world: &Arc<World>, pos: BlockPos) {
129 self.weathering.change_over_time(state, world, pos);
130 }
131
132 fn is_pathfindable(
133 &self,
134 _state: BlockStateId,
135 _computation_type: PathComputationType,
136 ) -> bool {
137 false
138 }
139}
140
141fn connects_to(
143 world: &dyn LevelReader,
144 neighbor_state: BlockStateId,
145 neighbor_pos: BlockPos,
146 direction: Direction,
147) -> bool {
148 let neighbor_block = neighbor_state.get_block();
149 let excluded = is_excluded_for_connection(neighbor_block);
150 (!excluded && world.is_face_sturdy(neighbor_state, neighbor_pos, direction.opposite()))
151 || neighbor_block.has_tag(&BlockTag::BARS)
152 || neighbor_block.has_tag(&BlockTag::WALLS)
153 || neighbor_block.has_tag(&BlockTag::C_GLASS_PANES)
154}
155
156pub fn get_connection_state(block: BlockRef, world: &World, pos: &BlockPos) -> BlockStateId {
158 let mut state = block.default_state();
159
160 let north_pos = Direction::North.relative(*pos);
162 let north_state = world.get_block_state(north_pos);
163 let connects_north = connects_to(world, north_state, north_pos, Direction::North);
164 state = state.set_value(&NORTH, connects_north);
165
166 let east_pos = Direction::East.relative(*pos);
168 let east_state = world.get_block_state(east_pos);
169 let connects_east = connects_to(world, east_state, east_pos, Direction::East);
170 state = state.set_value(&EAST, connects_east);
171
172 let south_pos = Direction::South.relative(*pos);
174 let south_state = world.get_block_state(south_pos);
175 let connects_south = connects_to(world, south_state, south_pos, Direction::South);
176 state = state.set_value(&SOUTH, connects_south);
177
178 let west_pos = Direction::West.relative(*pos);
180 let west_state = world.get_block_state(west_pos);
181 let connects_west = connects_to(world, west_state, west_pos, Direction::West);
182 state = state.set_value(&WEST, connects_west);
183
184 state
185}
186
187pub fn update_shape(
188 world: &dyn LevelReader,
189 state: BlockStateId,
190 neighbor_state: BlockStateId,
191 neighbor_pos: BlockPos,
192 direction: Direction,
193) -> BlockStateId {
194 match direction {
195 Direction::North => {
196 let connects = connects_to(world, neighbor_state, neighbor_pos, Direction::North);
197 state.set_value(&NORTH, connects)
198 }
199 Direction::East => {
200 let connects = connects_to(world, neighbor_state, neighbor_pos, Direction::East);
201 state.set_value(&EAST, connects)
202 }
203 Direction::South => {
204 let connects = connects_to(world, neighbor_state, neighbor_pos, Direction::South);
205 state.set_value(&SOUTH, connects)
206 }
207 Direction::West => {
208 let connects = connects_to(world, neighbor_state, neighbor_pos, Direction::West);
209 state.set_value(&WEST, connects)
210 }
211 Direction::Up | Direction::Down => state,
212 }
213}