1use super::GridPos;
3pub(crate) use super::autotile::*;
4use super::schematic::Schematic;
5use crate::block::State;
6use crate::block::content::Type;
7use crate::color_mapping::BLOCK2COLOR;
8use crate::data::map::Registrar;
9use crate::team::Team;
10pub(crate) use crate::utils::*;
11use crate::{Map, content::Content};
12use crate::{
13 block::Rotation,
14 data::map::{ThinBloc, ThinMapData},
15};
16use atools::prelude::*;
17use either::Either;
18use fimg::{BlendingOverlay, BlendingOverlayAt, uninit};
19use std::hint::unlikely;
20use std::iter::successors;
21use std::ops::Coroutine;
22use std::pin::Pin;
23
24include!(concat!(env!("OUT_DIR"), "/full.rs"));
25include!(concat!(env!("OUT_DIR"), "/quar.rs"));
26include!(concat!(env!("OUT_DIR"), "/eigh.rs"));
27
28#[derive(Debug, Copy, Clone)]
29#[repr(u8)]
30pub enum Scale {
31 Full,
32 Quarter,
34 Eigth,
35}
36
37impl Scale {
38 #[must_use]
39 pub const fn px(self) -> u8 {
40 match self {
41 Self::Full => 32,
42 Self::Quarter => 32 / 4,
43 Self::Eigth => 32 / 8,
44 }
45 }
46}
47
48impl std::ops::Mul<u32> for Scale {
49 type Output = u32;
50 fn mul(self, rhs: u32) -> u32 {
51 self.px() as u32 * rhs
52 }
53}
54
55#[macro_export]
56macro_rules! load {
57 (raw $name: literal, $scale:expr) => {
58 paste::paste! { match $scale {
59 $crate::data::renderer::Scale::Quarter => $crate::data::renderer::quar::[<$name:snake:upper>],
60 $crate::data::renderer::Scale::Eigth => $crate::data::renderer::eigh::[<$name:snake:upper>],
61 $crate::data::renderer::Scale::Full => $crate::data::renderer::full::[<$name:snake:upper>],
62 }
63 } };
64 (8x $name:literal) => { paste::paste! {
65 car::map!([
66 load!([<$name 1>]),
67 load!([<$name 2>]),
68 load!([<$name 3>]),
69 load!([<$name 4>]),
70 load!([<$name 5>]),
71 load!([<$name 6>]),
72 load!([<$name 7>]),
73 load!([<$name 8>]),
74 ], |x| car::map!(x, DynImage::from))
75 } };
76 ($name:literal, $scale:expr) => { paste::paste! {
77 #[allow(unused_unsafe)] unsafe { match $scale {
78 $crate::data::renderer::Scale::Quarter => &$crate::data::renderer::quar::[<$name:snake:upper>],
79 $crate::data::renderer::Scale::Eigth => &$crate::data::renderer::eigh::[<$name:snake:upper>],
80 $crate::data::renderer::Scale::Full => &$crate::data::renderer::full::[<$name:snake:upper>],
81 }.mapped($crate::utils::Cow::Ref) }
82 } };
83 ($name: ident) => { paste::paste! {
84 [$crate::data::renderer::full::[<$name:snake:upper>].copy(), $crate::data::renderer::quar::[<$name:snake:upper>].copy(), $crate::data::renderer::eigh::[<$name:snake:upper>].copy()]
85 } };
86 ($name: literal) => { paste::paste! {
87 [$crate::data::renderer::full::[<$name:snake:upper>].copy(), $crate::data::renderer::quar::[<$name:snake:upper>].copy(), $crate::data::renderer::eigh::[<$name:snake:upper>].copy()]
88 } };
89 (from $v:ident which is [$($k:literal $(|)?)+], $scale: ident) => {
90 $crate::data::renderer::load!($scale -> match $v {
91 $($k => $k,)+
92 })
93 };
94 ($scale:ident -> match $v:ident { $($k:pat => $nam:literal $(,)?)+ }) => {
96 match $v {
97 $($k => $crate::data::renderer::load!($nam, $scale),)+
98 #[allow(unreachable_patterns)]
99 n => unreachable!("{n:?}"),
100 }
101 };
102 (concat $x:literal => $v:ident which is [$($k:literal $(|)?)+], $scale: ident) => { paste::paste! {
103 match $v {
104 $($k =>
105 #[allow(unused_unsafe)] unsafe { (match $scale {
106 $crate::data::renderer::Scale::Quarter => &$crate::data::renderer::quar::[<$k:snake:upper _ $x:snake:upper>],
107 $crate::data::renderer::Scale::Eigth => &$crate::data::renderer::eigh::[<$k:snake:upper _ $x:snake:upper>],
108 $crate::data::renderer::Scale::Full => &$crate::data::renderer::full::[<$k:snake:upper _ $x:snake:upper>],
109 }.mapped($crate::utils::Cow::Ref)) },
110 )+
111 #[allow(unreachable_patterns)]
112 n => unreachable!("{n:?}"),
113 }
114 } };
115}
116pub(crate) use load;
117
118pub trait Renderable {
120 #[must_use = "i did so much work for you"]
122 fn render(&self) -> Image<Vec<u8>, 3>;
123}
124
125impl Renderable for Schematic {
126 fn render(&self) -> Image<Vec<u8>, 3> {
137 let scale = if self.width + self.height > 500 {
138 Scale::Quarter
139 } else {
140 Scale::Full
141 };
142 let x_fac = cfg!(feature = "square") as u32
145 * self.height.checked_sub(self.width).unwrap_or(0) as u32
146 + 2;
147 let y_fac = cfg!(feature = "square") as u32
148 * self.width.checked_sub(self.height).unwrap_or(0) as u32
149 + 2;
150 let mut bg = unsafe {
151 load!("metal-floor", scale).repeated(
152 scale * (self.width + x_fac as usize) as u32,
153 scale * (self.height + y_fac as usize) as u32,
154 )
155 };
156 let mut canvas = Image::alloc(bg.width(), bg.height());
157 for (GridPos(x, y), tile) in self.block_iter() {
158 let ctx = tile.block.wants_context().then(|| {
159 let pctx = PositionContext {
160 position: GridPos(x, y),
161 width: self.width,
162 height: self.height,
163 };
164 let (cross, corners) = self.cross(&pctx);
165 RenderingContext {
166 cross,
167 corners,
168 position: pctx,
169 }
170 });
171 let x = x as u32 - ((tile.block.get_size() - 1) / 2) as u32;
172 let y = self.height as u32 - y as u32 - ((tile.block.get_size() / 2) + 1) as u32;
173 unsafe {
174 canvas.as_mut().overlay_at(
175 &tile.image(
176 ctx.as_ref(),
177 tile.get_rotation().unwrap_or(Rotation::Up),
178 scale,
179 ),
180 scale * (x + x_fac / 2),
181 scale * (y + y_fac / 2),
182 )
183 };
184 }
185 for (p, b) in self.block_iter() {
186 let Some(&State::Point(mut relative)) = b.get_state() else {
187 continue;
188 };
189 let directional = matches!(b.block.name(), "duct-bridge" | "reinforced-bridge-conduit");
190 if false {
191 let offset = match b.rot {
192 Rotation::Right => (1, 0),
193 Rotation::Down => (0, -1),
194 Rotation::Left => (-1, 0),
195 Rotation::Up => (0, 1),
196 };
197 relative = successors(Some(offset), |&(x, y)| Some((x + offset.0, y + offset.1)))
198 .take(4)
199 .find(|x| {
200 self.get((p.0 as i32 + x.0) as _, (p.1 as i32 + x.1) as _)
201 .ok()
202 .flatten()
203 .is_some_and(|x| x.block == b.block)
204 });
205 }
206 if let Some(relative) = relative
207 && relative != (0, 0)
208 && let n @ ("bridge-conveyor" | "bridge-conduit" | "phase-conveyor"
209 | "phase-conduit") = b.block.name()
210 && let Ok(Some(x)) = self.get(
211 (p.0 as i32 + relative.0) as _,
212 (p.1 as i32 + relative.1) as _,
213 )
214 && x.block.name() == n
215 {
216 let mut bridge = load!(concat "bridge" => n which is ["bridge-conveyor" | "bridge-conduit" | "phase-conveyor" | "phase-conduit" | "duct-bridge" | "reinforced-bridge-conduit"], scale);
217
218 if relative.1 != 0 {
228 bridge =
230 Image::build(bridge.height(), bridge.width()).buf(bridge.take_buffer());
231 }
232 let arrow = load!(concat "arrow" => n which is ["bridge-conveyor"| "bridge-conduit" | "phase-conveyor" | "phase-conduit" | "duct-bridge" | "reinforced-bridge-conduit"], scale);
233 for index in if relative.0 > 0 {
244 Either::Right(
245 scale.px() as i32 * 2..scale.px() as i32 * relative.0 + scale.px() as i32,
246 )
247 } else {
248 Either::Left(
249 relative.0 * scale.px() as i32 + scale.px() as i32 * 2..scale.px() as i32,
250 )
251 } {
252 unsafe {
253 canvas.overlay_blended_at(
254 &bridge,
255 (p.0 as i32 * scale.px() as i32 + index) as u32,
256 scale * (self.height as u32 - p.1 as u32 - 1 + y_fac / 2),
257 )
258 };
259 }
260
261 for index in if relative.1 > 0 {
262 Either::Right(
263 -(scale.px() as i32) - scale.px() as i32 * (relative.1 - 1)
264 ..-(scale.px() as i32),
265 )
266 } else {
267 Either::Left(0..(-relative.1 - 1) * scale.px() as i32)
268 } {
269 let y =
270 ((self.height as i32 - p.1 as i32 + 1) * scale.px() as i32 + index) as u32;
271
272 unsafe {
273 canvas.overlay_blended_at(&bridge, scale * (p.0 as u32 + x_fac / 2), y)
274 };
275 }
276 if relative.0 != 0 {
277 unsafe {
278 canvas.overlay_blended_at(
279 &arrow.rotated(if directional {
280 b.rot.rotated(false).count()
281 } else if relative.0 < 0 {
282 2
283 } else {
284 0
285 }),
286 scale.px() as u32
287 + ((scale * p.0 as u32).cast_signed()
288 + (scale.px() as i32 * relative.0 / 2))
289 as u32,
290 (y_fac / 2 + scale * (self.height as u32 - p.1 as u32)) - 1,
291 )
292 };
293 } else {
294 unsafe {
295 canvas.overlay_blended_at(
296 &arrow.rotated(if directional {
297 b.rot.rotated(false).count()
298 } else if relative.1 < 0 {
299 1
300 } else {
301 3
302 }),
303 (scale * p.0 as u32) + (x_fac / 2 + scale.px() as u32) - 1,
304 scale.px() as u32
305 + ((scale * (self.height as u32 - p.1 as u32 - 1)).cast_signed()
306 + (scale.px() as i32 * -relative.1 / 2))
307 as u32,
308 );
309 }
310 }
311 }
312 }
313
314 if matches!(scale, Scale::Full) {
315 ImageUtils::shadow(&mut canvas);
316 unsafe { bg.overlay_blended(&canvas) };
317 } else {
318 unsafe { bg.overlay(&canvas) };
319 }
320 bg
321 }
322}
323
324impl Renderable for Map {
325 #[implicit_fn::implicit_fn]
327 fn render(&self) -> Image<Vec<u8>, 3> {
328 let scale = if self.width + self.height < 600 {
329 Scale::Full
330 } else if self.width + self.height < 4000 {
331 Scale::Quarter
332 } else {
333 Scale::Eigth
334 };
335 use crate::data::map::table;
336 let mut img = uninit::Image::<_, 3>::new(
337 (scale * self.width as u32).try_into().unwrap(),
338 (scale * self.height as u32).try_into().unwrap(),
339 );
340 for y in 0..self.height {
342 for x in 0..self.width {
343 let j = x + self.width * y;
345 let tile = unsafe { self.tiles.get_unchecked(j) };
346 let y = self.height - y - 1;
347 if [
350 Type::ColoredFloor,
351 Type::MetalTiles1,
352 Type::MetalTiles2,
353 Type::MetalTiles3,
354 Type::MetalTiles4,
355 Type::MetalTiles5,
356 Type::MetalTiles6,
357 Type::MetalTiles7,
358 Type::MetalTiles8,
359 Type::MetalTiles9,
360 Type::MetalTiles10,
361 Type::MetalTiles11,
362 Type::MetalTiles12,
363 ]
364 .contains(&tile.floor)
365 {
366 let mask = crate::data::autotile::nbors(
367 self.corners(j).map(_.map(_.floor)),
368 self.cross(j).map(_.map(_.floor)),
369 tile.floor,
370 );
371 let i =
372 &crate::data::autotile::select(tile.floor.get_name(), mask)[scale as usize];
373 if tile.floor == Type::ColoredFloor {
374 let mut i = i.boxed();
375 unsafe {
376 img.overlay_at(
377 &i.as_mut()
378 .tint(tile.nd.skip::<3>().take::<3>().into())
379 .as_ref(),
380 scale * x as u32,
381 scale * y as u32,
382 )
383 };
384 } else {
385 unsafe { img.overlay_at(i, scale * x as u32, scale * y as u32) };
386 }
387 } else {
388 unsafe {
389 img.overlay_at(
390 &table(tile.floor, scale),
391 scale * x as u32,
392 scale * y as u32,
393 );
394 }
395 }
396 macro_rules! f {
397 ($($x: literal)+) => { paste::paste!{
398 ([$(load!([<rune _ overlay $x>] ),)+], [$(load!([<rune _ overlay _ crux $x>] ),)+])
399 }};
400 }
401 const RUNES: ([[Image<&[u8], 4>; 3]; 109], [[Image<&[u8], 4>; 3]; 109]) = f![0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108];
402
403 if tile.has_ore() {
404 match tile.ore {
405 Type::RuneOverlay => unsafe {
406 img.overlay_at(
407 &RUNES.0[tile.nd[2] as usize][scale as usize],
408 scale * x as u32,
409 scale * y as u32,
410 );
411 },
412 Type::RuneOverlayCrux => unsafe {
413 img.overlay_at(
414 &RUNES.1[tile.nd[2] as usize][scale as usize],
415 scale * x as u32,
416 scale * y as u32,
417 );
418 },
419 Type::CharacterOverlay | Type::CharacterOverlayWhite => {
420 macro_rules! f {
421 ($($x: literal)+) => { paste::paste!{
422 [$(load!([<character _ overlay $x>] ),)+]
423 }};
424 }
425
426 const LETTERS: [[Image<&[u8], 4>; 3]; 64] = f![0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63];
427 unsafe {
428 img.overlay_at(
429 LETTERS[(tile.nd[2] & 0x3f) as usize][scale as usize]
430 .mapped(image::Cow::Ref)
431 .rotate(4 - (tile.nd[2] >> 6)),
432 scale * x as u32,
433 scale * y as u32,
434 )
435 };
436 }
437 ore => unsafe {
438 img.overlay_at(&table(ore, scale), scale * x as u32, scale * y as u32);
439 },
440 }
441 }
442 }
443 }
444 let mut img = unsafe { img.assume_init() };
445 for y in 0..self.height {
447 for x in 0..self.width {
448 let j = x + self.width * y;
449 let tile = unsafe { self.tiles.get_unchecked(j) };
450 let y = self.height - y - 1;
451 if let Some(build) = tile.build() {
452 let s = build.block.get_size();
453 let x = x
454 - (match s {
455 1 | 2 => 0,
456 3 | 4 => 1,
457 5 | 6 => 2,
458 7 | 8 => 3,
459 9 => 4,
460 _ => unsafe { std::hint::unreachable_unchecked() },
462 }) as usize;
463 let y = y
464 - (match s {
465 1 => 0,
466 2 | 3 => 1,
467 4 | 5 => 2,
468 6 | 7 => 3,
469 8 | 9 => 4,
470 _ => unsafe { std::hint::unreachable_unchecked() },
472 }) as usize;
473 if unlikely(matches!(
474 build.block.name,
475 Type::ColoredWall | Type::MetalWall1 | Type::MetalWall2 | Type::MetalWall3
476 )) {
477 let mask = crate::data::autotile::nbors(
478 self.corners(j)
479 .map(|x| x.and_then(|x| x.build().map(|b| b.block.name))),
480 self.cross(j)
481 .map(|x| x.and_then(|x| x.build().map(|b| b.block.name))),
482 build.block.name,
483 );
484 let i =
485 crate::data::autotile::select(build.block.name(), mask)[scale as usize];
486 if build.block.name == Type::ColoredWall {
487 unsafe {
488 img.overlay_at(
489 &i.boxed()
490 .as_mut()
491 .tint(tile.nd.skip::<3>().take::<3>().into())
492 .as_ref(),
493 scale * x as u32,
494 scale * y as u32,
495 )
496 };
497 } else {
498 unsafe { img.overlay_at(&i, scale * x as u32, scale * y as u32) };
499 }
500 continue;
501 }
502 let ctx = build.block.wants_context().then(|| {
503 let pctx = PositionContext {
504 position: GridPos(x, y),
505 width: self.width,
506 height: self.height,
507 };
508 RenderingContext {
509 cross: self
510 .cross(j)
511 .map(|b| b.and_then(|b| Some((b.get_block()?, b.get_rotation()?)))),
512 corners: Default::default(),
513 position: pctx,
514 }
515 });
516 unsafe {
517 img.as_mut().overlay_at(
518 &tile.build_image(ctx.as_ref(), scale),
519 scale * x as u32,
520 scale * y as u32,
521 )
522 };
523 }
524 }
525 }
526 for entity in &self.entities {
528 let (x, y) = (
530 entity.state.position.0 as u32,
531 self.height as u32 - entity.state.position.1 as u32 - 1,
532 );
533 if x < 10 || x as usize > self.width - 10 || y < 10 || y as usize > self.height - 10 {
534 continue;
535 }
536 unsafe {
537 img.as_mut()
538 .overlay_at(&entity.draw(scale), scale * x, scale * y)
539 };
540 }
541 img
542 }
543}
544
545#[test]
546fn all_blocks() {
547 use crate::block::content::Type;
548 use crate::content::Content;
549 for t in 19..Type::WorldMessage as u16 {
550 let t = Type::try_from(t).unwrap();
551 if matches!(t, |Type::Empty| Type::SlagCentrifuge
552 | Type::HeatReactor
553 | Type::LegacyMechPad
554 | Type::LegacyUnitFactory
555 | Type::LegacyUnitFactoryAir
556 | Type::LegacyUnitFactoryGround
557 | Type::CommandCenter)
558 {
559 continue;
560 }
561 let name = t.get_name();
562 let t = crate::block::BLOCK_REGISTRY.get(name).unwrap();
563 let _ = t.image(
564 None,
565 Some(&RenderingContext {
566 corners: [None; 4],
567 cross: [None; 4],
568 position: PositionContext {
569 position: GridPos(0, 0),
570 width: 5,
571 height: 5,
572 },
573 }),
574 Rotation::Up,
575 Scale::Quarter,
576 );
577 }
578}
579
580pub fn draw_units(
581 map: &mut crate::data::map::MapReader,
582 mut img: Image<&mut [u8], 3>,
583 size: (u16, u16),
584) -> Result<(), super::map::ReadError> {
585 use std::ops::CoroutineState::*;
586 let scale = if size.0 + size.1 < 2000 {
587 Scale::Quarter
588 } else {
589 Scale::Eigth
590 };
591
592 let mut co = map.entities()?;
593 let n = match Pin::new(&mut co).resume(()) {
594 Yielded(crate::data::map::EntityData::Length(x)) => x,
595 Complete(Err(e)) => return Err(e),
596 _ => unreachable!(),
597 };
598 'out: {
599 for _ in 0..n {
600 match Pin::new(&mut co).resume(()) {
601 Yielded(crate::data::map::EntityData::Data(entity)) => {
602 let (x, y) = (
604 entity.state.position.0 as u32,
605 size.1 as u32 - entity.state.position.1 as u32 - 1,
606 );
607 if x < 10
608 || x as usize > size.0 as usize - 10
609 || y < 10
610 || y as usize > size.1 as usize - 10
611 {
612 continue;
613 }
614 unsafe {
615 img.as_mut()
616 .overlay_at(&entity.draw(scale), scale * x, scale * y)
617 };
618 }
619 Complete(Err(e)) => return Err(e),
620 Complete(Ok(())) => break 'out,
621 x => unreachable!("{x:?}"),
622 }
623 }
624 match Pin::new(&mut co).resume(()) {
625 Complete(Ok(())) => (),
626 _ => unreachable!(),
627 };
628 }
629 Ok(())
630}
631
632pub fn draw_map_single(
638 map: &mut crate::data::map::MapReader,
639 r: Registrar,
640) -> Result<(Image<Box<[u8]>, 3>, (u16, u16)), super::map::ReadError> {
641 use std::ops::CoroutineState::*;
642 let mut co = map.thin_map(r)?;
643 let (w, h) = match Pin::new(&mut co).resume(()) {
644 Yielded(ThinMapData::Init { width, height }) => (width, height),
645 Complete(Err(x)) => return Err(x),
646 _ => unreachable!(),
647 };
648 let scale = if w + h < 2000 {
649 Scale::Quarter
650 } else {
651 Scale::Eigth
652 };
653 let mut img = uninit::Image::<_, 3>::new(
654 (scale * w as u32).try_into().unwrap(),
655 (scale * h as u32).try_into().unwrap(),
656 );
657 use crate::data::map::table;
658 for y in 0..h {
660 for x in 0..w {
661 let (floor, ore) = match Pin::new(&mut co).resume(()) {
662 Yielded(ThinMapData::Tile { floor, ore }) => (floor, ore),
663 Complete(Err(x)) => return Err(x),
664 _ => unreachable!(),
665 };
666 let y = h - y - 1;
667 unsafe { img.overlay_at(&table(floor, scale), scale * x as u32, scale * y as u32) };
669 if ore != Type::Air {
670 unsafe {
671 img.overlay_at(&table(ore, scale), scale * x as u32, scale * y as u32);
672 }
673 }
674 }
675 }
676 let mut img = unsafe { img.assume_init() }.boxed();
677 let mut i = 0;
678 while i < (w as usize * h as usize) {
679 let mut draw = |i, r, b: &'static crate::block::Block| {
680 let x = i % w as usize;
681 let y = i / w as usize;
682 let y = h as usize - y - 1;
683 let s = b.get_size();
684 let x = x
685 - (match s {
686 1 | 2 => 0,
687 3 | 4 => 1,
688 5 | 6 => 2,
689 7 | 8 => 3,
690 9 => 4,
691 _ => unsafe { std::hint::unreachable_unchecked() },
693 }) as usize;
694 let y = y
695 - (match s {
696 1 => 0,
697 2 | 3 => 1,
698 4 | 5 => 2,
699 6 | 7 => 3,
700 8 | 9 => 4,
701 _ => unsafe { std::hint::unreachable_unchecked() },
703 }) as usize;
704 let ctx = b.wants_context().then(|| {
705 let pctx = PositionContext {
706 position: GridPos(x, y),
707 width: w as usize,
708 height: h as usize,
709 };
710 RenderingContext {
711 corners: [None; 4],
712 cross: [None; 4], position: pctx,
714 }
715 });
716 unsafe {
717 img.as_mut().overlay_at(
718 &b.image(None, ctx.as_ref(), r, scale),
719 scale * x as u32,
720 scale * y as u32,
721 )
722 };
723 };
724 match Pin::new(&mut co).resume(()) {
725 Yielded(ThinMapData::Bloc(ThinBloc::Many(None, n))) => {
726 i += n as usize;
727 }
728 Yielded(ThinMapData::Bloc(ThinBloc::Build(r, bloc, _))) => {
729 draw(i, r, bloc);
730 }
731 Yielded(ThinMapData::Bloc(ThinBloc::Many(Some(bloc), n))) => {
732 for i in i..=i + n as usize {
733 draw(i, Rotation::Up, bloc);
734 }
735 i += n as usize;
736 }
737 Complete(Err(x)) => return Err(x),
738 x => unreachable!("{x:?}"),
739 }
740 i += 1;
741 }
742 match Pin::new(&mut co).resume(()) {
743 Complete(Ok(())) => (),
744 f => unreachable!("{f:?}"),
745 };
746
747 Ok((img, (w, h)))
748}
749
750pub fn draw_map_simple(
753 map: &mut crate::data::map::MapReader,
754 r: Registrar,
755) -> Result<(Image<Box<[u8]>, 3>, (u16, u16)), super::map::ReadError> {
756 use std::ops::CoroutineState::*;
757 let mut co = map.thin_map(r)?;
758 let (w, h) = match Pin::new(&mut co).resume(()) {
759 Yielded(ThinMapData::Init { width, height }) => (width, height),
760 Complete(Err(x)) => return Err(x),
761 _ => unreachable!(),
762 };
763 let mut img = uninit::Image::<u8, 3>::new(
764 (w as u32).try_into().unwrap(),
765 (h as u32).try_into().unwrap(),
766 );
767 for y in 0..h {
769 for x in 0..w {
770 let (floor, ore) = match Pin::new(&mut co).resume(()) {
771 Yielded(ThinMapData::Tile { floor, ore }) => (floor, ore),
772 Complete(Err(x)) => return Err(x),
773 _ => unreachable!(),
774 };
775 let t = (ore != Type::Air).then_some(ore).unwrap_or(floor);
776 let y = h - y - 1;
777 let i1 = img.at(x as u32, y as u32) as usize;
778 unsafe {
779 img.slice(i1..i1 + 3)
780 .write_copy_of_slice(&BLOCK2COLOR[t as u16 as usize][..])
781 };
782 }
783 }
784 let mut img = unsafe { img.assume_init() }.boxed();
785 let mut i = 0;
786 while i < (w as usize * h as usize) {
787 let mut draw = |i, b: &'static crate::block::Block, team: Team| {
788 let x = i % w as usize;
789 let y = i / w as usize;
790 let y = h as usize - y - 1;
791 let s = b.get_size();
792 let x = x
793 - (match s {
794 1 | 2 => 0,
795 3 | 4 => 1,
796 5 | 6 => 2,
797 7 | 8 => 3,
798 9 => 4,
799 _ => unsafe { std::hint::unreachable_unchecked() },
801 }) as usize;
802 let y = y
803 - (match s {
804 1 => 0,
805 2 | 3 => 1,
806 4 | 5 => 2,
807 6 | 7 => 3,
808 8 | 9 => 4,
809 _ => unsafe { std::hint::unreachable_unchecked() },
811 }) as usize;
812 for x in x..(x as usize + s as usize).min(w as usize) {
813 for y in y..(y as usize + s as usize).min(h as usize) {
814 unsafe {
815 img.set_pixel(x as u32, y as u32, <[u8; 3]>::from(team.color()));
816 }
817 }
818 }
819 };
820 match Pin::new(&mut co).resume(()) {
821 Yielded(ThinMapData::Bloc(ThinBloc::Many(None, n))) => {
822 i += n as usize;
823 }
824 Yielded(ThinMapData::Bloc(ThinBloc::Build(_, bloc, t))) => {
825 draw(i, bloc, t);
826 }
827 Yielded(ThinMapData::Bloc(ThinBloc::Many(Some(bloc), n))) => {
828 for i in i..=i + n as usize {
829 draw(i, bloc, Team::DERELICT);
830 }
831 i += n as usize;
832 }
833 Complete(Err(x)) => return Err(x),
834 x => unreachable!("{x:?}"),
835 }
836 i += 1;
837 }
838 match Pin::new(&mut co).resume(()) {
839 Complete(Ok(())) => (),
840 f => unreachable!("{f:?}"),
841 };
842
843 Ok((img, (w, h)))
844}