1use crate::block::simple::*;
3use crate::block::*;
4use crate::content;
5use crate::data::autotile::tile;
6use crate::data::dynamic::DynType;
7use crate::item;
8
9make_simple!(
10 ConveyorBlock,
11 |_, name, _, ctx: Option<&RenderingContext>, rot, s| tile(ctx.unwrap(), name, rot, s),
12 |_, buff: &mut DataRead| {
13 let amount = buff.read_i32()?;
21 for _ in 0..amount {
22 buff.skip(4)?;
23 }
24 Ok(())
25 }
26);
27
28make_simple!(
29 DuctBlock,
30 |_, name, _, ctx: Option<&RenderingContext>, rot, s| tile(ctx.unwrap(), name, rot, s),
31 |_, buff: &mut DataRead| {
32 buff.skip(1)
35 }
36);
37
38make_simple!(JunctionBlock => |_, buff| { read_directional_item_buffer(buff) });
39make_simple!(SimpleDuctBlock, |_, name, _, _, rot: Rotation, s| {
40 let mut base = load!("duct-base", s);
41 let mut top = load!(from name which is ["overflow-duct" "underflow-duct"], s);
42 unsafe { top.rotate(rot.rotated(false).count()) };
44 unsafe { base.overlay(&top) };
46 base
47});
48
49fn draw_stack(
50 _: &StackConveyor,
51 name: &str,
52 _: Option<&State>,
53 ctx: Option<&RenderingContext>,
54 rot: Rotation,
55 s: Scale,
56) -> ImageHolder<4> {
57 let ctx = ctx.unwrap();
58 let mask = mask(ctx, rot, name);
59 #[rustfmt::skip]
60 let edge = |n: u8| {
61 match n {
62 0 => load!(concat "edge-0" => name which is ["surge-conveyor" | "plastanium-conveyor"], s),
63 1 => load!(concat "edge-1" => name which is ["surge-conveyor" | "plastanium-conveyor"], s),
64 2 => load!(concat "edge-2" => name which is ["surge-conveyor" | "plastanium-conveyor"], s),
65 _ => load!(concat "edge-3" => name which is ["surge-conveyor" | "plastanium-conveyor"], s)
66 }
67 };
68 let edgify = |skip, to: &mut ImageHolder<4>| {
69 for i in 0..4 {
70 if i == skip {
71 continue;
72 }
73 unsafe { to.overlay(&edge(i)) };
74 }
75 };
76 let gimme = |n: u8| match n {
77 0 => load!(concat 0 => name which is ["surge-conveyor" | "plastanium-conveyor"], s),
78 1 => load!(concat 1 => name which is ["surge-conveyor" | "plastanium-conveyor"], s),
79 _ => load!("plastanium-conveyor-2", s),
80 };
81 let empty = ctx.cross[rot.count() as usize].map_or(true, |(v, _)| v.name.get_name() != name);
82 if rot.mirrored(true, true).mask() == mask && empty && name != "surge-conveyor" {
84 let mut base = gimme(2);
86 edgify(rot.mirrored(true, true).rotated(false).count(), &mut base);
87 base
88 } else if mask == B0000 && empty {
89 let mut base = gimme(0);
91 unsafe { base.rotate(rot.rotated(false).count()) };
92 edgify(5, &mut base);
93 base
94 } else if mask == B0000 {
95 let mut base = gimme(1);
97 edgify(rot.rotated(false).count(), &mut base);
98 base
99 } else {
100 let mut base = gimme(0);
102 let going = rot.rotated(false).count();
103 unsafe { base.rotate(going) };
104 for [r, i] in [[3, 0b1000], [0, 0b0100], [1, 0b0010], [2, 0b0001]] {
105 if (mask.into_u8() & i) == 0 && (going != r || empty) {
106 unsafe { base.overlay(&edge(r)) };
107 }
108 }
109 base
110 }
111}
112
113make_simple!(
114 StackConveyor,
115 draw_stack,
116 |_, buff: &mut DataRead| buff.skip(8)
120);
121make_simple!(
122 SurgeRouter,
123 |_, _, _, _, r: Rotation, s| {
124 let mut base = load!("surge-router", s);
125 let mut top = load!("top", s);
126 unsafe { top.rotate(r.rotated(false).count()) };
127 unsafe { base.overlay(&top) };
128 base
129 },
130 |_, buff: &mut DataRead| buff.skip(2)
131);
132make_simple!(UnitCargoLoader => |_, buff: &mut DataRead| buff.skip(4));
134
135pub struct ItemBlock {
136 size: u8,
137 symmetric: bool,
138 build_cost: BuildCost,
139}
140
141impl ItemBlock {
142 #[must_use]
143 pub const fn new(size: u8, symmetric: bool, build_cost: BuildCost) -> Self {
144 assert!(size != 0, "invalid size");
145 Self {
146 size,
147 symmetric,
148 build_cost,
149 }
150 }
151
152 state_impl!(pub Option<item::Type>);
153}
154
155impl BlockLogic for ItemBlock {
156 impl_block!();
157
158 fn data_from_i32(&self, config: i32, _: GridPos) -> Result<DynData, DataConvertError> {
159 if config < 0 || config > i32::from(u16::MAX) {
160 return Err(DataConvertError::Custom(Box::new(ItemConvertError(config))));
161 }
162 Ok(DynData::Content(content::Type::Item, config as u16))
163 }
164
165 fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
166 match data {
167 DynData::Empty => Ok(Some(Self::create_state(None))),
168 DynData::Content(content::Type::Item, id) => Ok(Some(Self::create_state(Some(
169 ItemDeserializeError::forward(item::Type::try_from(id))?,
170 )))),
171 DynData::Content(have, ..) => Err(DeserializeError::Custom(Box::new(
172 ItemDeserializeError::ContentType(have),
173 ))),
174 _ => Err(DeserializeError::InvalidType {
175 have: data.get_type(),
176 expect: DynType::Content,
177 }),
178 }
179 }
180
181 fn mirror_state(&self, _: &mut State, _: bool, _: bool) {}
182
183 fn rotate_state(&self, _: &mut State, _: bool) {}
184
185 fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
186 Ok(Self::get_state(state).map_or(DynData::Empty, |item| {
187 DynData::Content(content::Type::Item, item.into())
188 }))
189 }
190
191 fn draw(
192 &self,
193 name: &str,
194 state: Option<&State>,
195 _: Option<&RenderingContext>,
196 rot: Rotation,
197 s: Scale,
198 ) -> ImageHolder<4> {
199 let mut p = load!(from name which is ["sorter" | "inverted-sorter" | "duct-router" | "duct-unloader" | "unit-cargo-unload-point" | "unloader" | "item-source"], s);
200 if let Some(state) = state
201 && let Some(item) = Self::get_state(state)
202 {
203 let mut top = load!(s -> match name {
204 "unit-cargo-unload-point" => "unit-cargo-unload-point-top",
205 "unloader" => "unloader-center",
206 _ => "center",
207 });
208 unsafe { p.overlay(top.tint(item.color())) };
209 if name == "duct-unloader" {
210 unsafe {
211 let mut x = load!("duct-unloader-arrows", s);
212 p.overlay(&*x.rotate(rot.rotated(false).count()));
213 }
214 }
215 return p;
216 }
217 if matches!(name, "duct-router" | "duct-unloader") {
218 let mut top = load!(s -> match name {
219 "duct-router" => "top",
220 "duct-unloader" => "duct-unloader-top",
221 });
222 unsafe { top.rotate(rot.rotated(false).count()) };
223 unsafe { p.overlay(&top) };
224 }
225 p
226 }
227
228 fn read(&self, b: &mut Build, buff: &mut DataRead) -> Result<(), DataReadError> {
239 match b.block.name() {
240 "duct-unloader" => {
241 let n = buff.read_i16()?;
242 if n != -1 {
243 b.state = Some(Self::create_state(item::Type::try_from(n as u16).ok()));
244 }
245 buff.skip(2)?;
246 }
247 "unit-cargo-unload-point" => {
248 b.state = Some(Self::create_state(
249 item::Type::try_from(buff.read_u16()?).ok(),
250 ));
251 buff.skip(1)?;
252 }
253 _ => {
254 b.state = Some(Self::create_state(
255 item::Type::try_from(buff.read_u16()?).ok(),
256 ));
257 }
258 }
259 Ok(())
260 }
261}
262
263#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
264#[error("invalid config ({0}) for item")]
265pub struct ItemConvertError(pub i32);
266
267#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
268pub enum ItemDeserializeError {
269 #[error("expected Item but got {0:?}")]
270 ContentType(content::Type),
271 #[error("target item not found")]
272 NotFound(#[from] item::TryFromU16Error),
273}
274
275impl ItemDeserializeError {
276 pub fn forward<T, E: Into<Self>>(result: Result<T, E>) -> Result<T, DeserializeError> {
277 match result {
278 Ok(v) => Ok(v),
279 Err(e) => Err(DeserializeError::Custom(Box::new(e.into()))),
280 }
281 }
282}
283
284pub struct BridgeBlock {
285 size: u8,
286 symmetric: bool,
287 build_cost: BuildCost,
288 range: u16,
289 ortho: bool,
290}
291
292type Point2 = (i32, i32);
293
294impl BridgeBlock {
295 #[must_use]
296 pub const fn new(
297 size: u8,
298 symmetric: bool,
299 build_cost: BuildCost,
300 range: u16,
301 ortho: bool,
302 ) -> Self {
303 assert!(size != 0, "invalid size");
304 assert!(range != 0, "invalid range");
305 Self {
306 size,
307 symmetric,
308 build_cost,
309 range,
310 ortho,
311 }
312 }
313
314 state_impl!(pub Option<Point2>);
315}
316
317impl BlockLogic for BridgeBlock {
318 impl_block!();
319
320 fn data_from_i32(&self, config: i32, pos: GridPos) -> Result<DynData, DataConvertError> {
321 let (x, y) = ((config >> 16) as i16, config as i16);
322 if x < 0 || y < 0 {
323 return Err(DataConvertError::Custom(Box::new(BridgeConvertError {
324 x,
325 y,
326 })));
327 }
328 let dx = i32::from(x) - pos.0 as i32;
329 let dy = i32::from(y) - pos.1 as i32;
330 Ok(DynData::Point2(dx, dy))
331 }
332
333 fn deserialize_state(&self, data: DynData) -> Result<Option<State>, DeserializeError> {
334 match data {
335 DynData::Empty => Ok(Some(Self::create_state(None))),
336 DynData::Point2(dx, dy) => {
337 if self.ortho {
338 if dx != 0 && dy != 0 {
341 return Ok(Some(Self::create_state(None)));
342 }
343 if dx > i32::from(self.range) || dx < -i32::from(self.range) {
344 return Ok(Some(Self::create_state(None)));
345 }
346 }
347 Ok(Some(Self::create_state(Some((dx, dy)))))
349 }
350 _ => Err(DeserializeError::InvalidType {
351 have: data.get_type(),
352 expect: DynType::Point2,
353 }),
354 }
355 }
356
357 fn mirror_state(&self, state: &mut State, horizontally: bool, vertically: bool) {
358 match Self::get_state_mut(state) {
359 None => (),
360 Some((dx, dy)) => {
361 if horizontally {
362 *dx = -*dx;
363 }
364 if vertically {
365 *dy = -*dy;
366 }
367 }
368 }
369 }
370
371 fn rotate_state(&self, state: &mut State, clockwise: bool) {
372 match Self::get_state_mut(state) {
373 None => (),
374 Some((dx, dy)) => {
375 let (cdx, cdy) = (*dx, *dy);
376 *dx = if clockwise { cdy } else { -cdy };
377 *dy = if clockwise { -cdx } else { cdx };
378 }
379 }
380 }
381
382 fn serialize_state(&self, state: &State) -> Result<DynData, SerializeError> {
383 match Self::get_state(state) {
384 None => Ok(DynData::Point2(-1, -1)),
385 Some((dx, dy)) => Ok(DynData::Point2(*dx, *dy)),
386 }
387 }
388
389 fn read(
408 &self,
409 t: &mut Build,
410 buff: &mut crate::data::DataRead,
411 ) -> Result<(), crate::data::ReadError> {
412 match t.block.name() {
413 "bridge-conveyor" => read_buffered_item_bridge(buff)?,
414 "phase-conveyor" | "phase-conduit" | "bridge-conduit" => read_item_bridge(buff)?,
415 "mass-driver" => buff.skip(9)?,
416 "payload-mass-driver" | "large-payload-mass-driver" => {
417 crate::block::payload::read_payload_block(buff)?;
418 buff.skip(19)?;
419 }
420 "duct-bridge" | "reinforced-bridge-conduit" => {}
422 n => unreachable!("{n}"), }
424 Ok(())
425 }
426
427 fn draw(
428 &self,
429 name: &str,
430 _: Option<&State>,
431 _: Option<&RenderingContext>,
432 r: Rotation,
433 s: Scale,
434 ) -> ImageHolder<4> {
435 match name {
436 "mass-driver" => {
437 let mut base = load!("mass-driver-base", s);
438 unsafe { base.overlay(&load!("mass-driver", s)) };
439 base
440 }
441 "duct-bridge" | "reinforced-bridge-conduit" => {
442 let mut base =
443 load!(from name which is ["duct-bridge" | "reinforced-bridge-conduit"], s);
444 let mut arrow = load!(
445 s -> match name {
446 "duct-bridge" => "duct-bridge-dir",
447 _ => "reinforced-bridge-conduit-dir",
448 }
449 );
450 unsafe { arrow.rotate(r.rotated(false).count()) };
451 unsafe { base.overlay(&arrow) };
452 base
453 }
454 _ => unreachable!(),
456 }
457 }
458}
459
460#[derive(Clone, Copy, Debug, Eq, PartialEq, thiserror::Error)]
461#[error("invalid coordinates ({x}, {y}) for bridge")]
462pub struct BridgeConvertError {
463 pub x: i16,
464 pub y: i16,
465}
466
467fn read_buffered_item_bridge(buff: &mut DataRead) -> Result<(), DataReadError> {
471 read_item_bridge(buff)?;
472 read_item_buffer(buff)
473}
474
475fn read_item_buffer(buff: &mut DataRead) -> Result<(), DataReadError> {
480 buff.skip(1)?;
481 let n = buff.read_u8()? as usize;
482 buff.skip(n * 8)
483}
484
485fn read_item_bridge(buff: &mut DataRead) -> Result<(), DataReadError> {
492 buff.skip(8)?;
493 let n = buff.read_u8()? as usize;
494 buff.skip((n * 4) + 1)
495}
496
497fn read_directional_item_buffer(buff: &mut DataRead) -> Result<(), DataReadError> {
503 for _ in 0..4 {
504 let _ = buff.read_u8()?;
505 let n = buff.read_u8()? as usize;
506 buff.skip(n * 8)?;
507 }
508 Ok(())
509}