1use glam::IVec3;
5
6use crate::geom::{Face, FaceSet};
7use crate::block;
8
9use super::World;
10
11
12impl World {
14
15 #[inline]
17 pub fn has_active_power(&mut self, pos: IVec3) -> bool {
18 Face::ALL.into_iter().any(|face| self.has_active_power_from(pos + face.delta(), face.opposite()))
19 }
20
21 #[inline]
23 pub fn has_passive_power(&mut self, pos: IVec3) -> bool {
24 Face::ALL.into_iter().any(|face| self.has_passive_power_from(pos + face.delta(), face.opposite()))
25 }
26
27 #[inline]
29 pub fn has_active_power_from(&mut self, pos: IVec3, face: Face) -> bool {
30 self.get_active_power_from(pos, face) > 0
31 }
32
33 #[inline]
35 pub fn has_passive_power_from(&mut self, pos: IVec3, face: Face) -> bool {
36 self.get_passive_power_from(pos, face) > 0
37 }
38
39 pub fn get_active_power_from(&mut self, pos: IVec3, face: Face) -> u8 {
41 let power = self.get_power_from(pos, face, true);
42 if power.indirect || !power.passive {
43 power.level
44 } else {
45 0
46 }
47 }
48
49 pub fn get_passive_power_from(&mut self, pos: IVec3, face: Face) -> u8 {
51 self.get_power_from(pos, face, true).level
52 }
53
54 fn get_power_from(&mut self, pos: IVec3, face: Face, test_block: bool) -> Power {
56
57 let Some((id, metadata)) = self.get_block(pos) else { return Power::OFF };
58
59 match id {
60 block::LEVER => self.get_lever_power_from(face, metadata),
61 block::BUTTON => self.get_button_power_from(face, metadata),
62 block::REPEATER_LIT => self.get_repeater_power_from(face, metadata),
63 block::REDSTONE_TORCH_LIT => self.get_redstone_torch_power_from(face, metadata),
64 block::REDSTONE => self.get_redstone_power_from(pos, face, metadata),
65 _ if test_block && block::material::is_opaque_cube(id) =>
67 self.get_block_power_from(pos, face),
68 _ => Power::OFF
70 }
71
72 }
73
74 fn get_block_power_from(&mut self, pos: IVec3, face: Face) -> Power {
76
77 let mut ret = Power { level: 0, indirect: false, passive: true };
80
81 for test_face in [Face::NegY, Face::PosY, Face::NegZ, Face::PosZ, Face::NegX, Face::PosX] {
83 if test_face != face {
84
85 let power = self.get_power_from(pos + test_face.delta(), test_face.opposite(), false);
89 if power.indirect {
91
92 if !power.passive && ret.passive {
93 ret.level = power.level;
94 ret.passive = false;
95 } else if power.passive == ret.passive && power.level > ret.level {
96 ret.level = power.level;
97 }
98
99 if !ret.passive && ret.level >= 15 {
101 break;
102 }
103
104 }
105
106 }
107 }
108
109 ret
110
111 }
112
113 fn get_lever_power_from(&mut self, face: Face, metadata: u8) -> Power {
114 if block::lever::is_active(metadata) {
115 if block::lever::get_face(metadata).map(|(f, _)| f) == Some(face) {
116 Power::ON_INDIRECT
117 } else {
118 Power::ON_DIRECT
119 }
120 } else {
121 Power::OFF
122 }
123 }
124
125 fn get_button_power_from(&mut self, face: Face, metadata: u8) -> Power {
126 if block::button::is_active(metadata) {
127 if block::button::get_face(metadata) == Some(face) {
128 Power::ON_INDIRECT
129 } else {
130 Power::ON_DIRECT
131 }
132 } else {
133 Power::OFF
134 }
135 }
136
137 fn get_repeater_power_from(&mut self, face: Face, metadata: u8) -> Power {
138 if block::repeater::get_face(metadata) == face {
139 Power::ON_INDIRECT
140 } else {
141 Power::OFF
142 }
143 }
144
145 fn get_redstone_torch_power_from(&mut self, face: Face, metadata: u8) -> Power {
146 if block::torch::get_face(metadata) == Some(face) {
147 Power::OFF
148 } else if face == Face::PosY {
149 Power::ON_INDIRECT
150 } else {
151 Power::ON_DIRECT
152 }
153 }
154
155 fn get_redstone_power_from(&mut self, pos: IVec3, face: Face, metadata: u8) -> Power {
156 if face == Face::PosY || metadata == 0 {
157 Power::OFF
158 } else if face == Face::NegY {
159 Power { level: metadata, indirect: true, passive: true }
160 } else {
161
162 let mut links = FaceSet::new();
163
164 let opaque_above = self.get_block(pos + IVec3::Y)
165 .map(|(above_id, _)| block::material::is_opaque_cube(above_id))
166 .unwrap_or(true);
167
168 for face in [Face::NegX, Face::PosX, Face::NegZ, Face::PosZ] {
169 let face_pos = pos + face.delta();
170 if self.is_linkable_from(face_pos, face.opposite()) {
171 links.insert(face);
172 } else {
173 if let Some((id, _)) = self.get_block(face_pos) {
174 if !block::material::is_opaque_cube(id) {
175 if self.is_linkable_from(face_pos - IVec3::Y, Face::PosY) {
176 links.insert(face);
177 }
178 } else if !opaque_above {
179 if self.is_linkable_from(face_pos + IVec3::Y, Face::NegY) {
180 links.insert(face);
181 }
182 }
183 }
184 }
185 }
186
187 let indirect = if links.is_empty() {
191 true
194 } else {
195 match face {
196 Face::NegZ => links.contains(Face::PosZ) && !links.contains_x(),
197 Face::PosZ => links.contains(Face::NegZ) && !links.contains_x(),
198 Face::NegX => links.contains(Face::PosX) && !links.contains_z(),
199 Face::PosX => links.contains(Face::NegX) && !links.contains_z(),
200 _ => unreachable!()
201 }
202 };
203
204 Power { level: metadata, indirect, passive: true }
205
206 }
207 }
208
209 fn is_linkable_from(&mut self, pos: IVec3, face: Face) -> bool {
212 if let Some((id, metadata)) = self.get_block(pos) {
213 match id {
214 block::LEVER |
215 block::BUTTON |
216 block::DETECTOR_RAIL |
217 block::WOOD_PRESSURE_PLATE |
218 block::STONE_PRESSURE_PLATE |
219 block::REDSTONE_TORCH |
220 block::REDSTONE_TORCH_LIT |
221 block::REDSTONE => true,
222 block::REPEATER |
223 block::REPEATER_LIT => {
224 let repeater_face = block::repeater::get_face(metadata);
225 face == repeater_face.opposite()
226 }
227 _ => false
228 }
229 } else {
230 false
231 }
232 }
233
234}
235
236
237#[derive(Debug)]
239struct Power {
240 level: u8,
242 indirect: bool,
244 passive: bool,
246}
247
248impl Power {
249
250 const OFF: Self = Self { level: 0, indirect: false, passive: false };
251 const ON_INDIRECT: Self = Self { level: 15, indirect: true, passive: false };
252 const ON_DIRECT: Self = Self { level: 15, indirect: false, passive: false };
253
254}