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 }
397 }
398 let mut img = unsafe { img.assume_init() };
399 for y in 0..self.height {
401 for x in 0..self.width {
402 let j = x + self.width * y;
403 let tile = unsafe { self.tiles.get_unchecked(j) };
404 let y = self.height - y - 1;
405 if let Some(build) = tile.build() {
406 let s = build.block.get_size();
407 let x = x
408 - (match s {
409 1 | 2 => 0,
410 3 | 4 => 1,
411 5 | 6 => 2,
412 7 | 8 => 3,
413 9 => 4,
414 _ => unsafe { std::hint::unreachable_unchecked() },
416 }) as usize;
417 let y = y
418 - (match s {
419 1 => 0,
420 2 | 3 => 1,
421 4 | 5 => 2,
422 6 | 7 => 3,
423 8 | 9 => 4,
424 _ => unsafe { std::hint::unreachable_unchecked() },
426 }) as usize;
427 if unlikely(matches!(
428 build.block.name,
429 Type::ColoredWall | Type::MetalWall1 | Type::MetalWall2 | Type::MetalWall3
430 )) {
431 let mask = crate::data::autotile::nbors(
432 self.corners(j)
433 .map(|x| x.and_then(|x| x.build().map(|b| b.block.name))),
434 self.cross(j)
435 .map(|x| x.and_then(|x| x.build().map(|b| b.block.name))),
436 build.block.name,
437 );
438 let i =
439 crate::data::autotile::select(build.block.name(), mask)[scale as usize];
440 if build.block.name == Type::ColoredWall {
441 unsafe {
442 img.overlay_at(
443 &i.boxed()
444 .as_mut()
445 .tint(tile.nd.skip::<3>().take::<3>().into())
446 .as_ref(),
447 scale * x as u32,
448 scale * y as u32,
449 )
450 };
451 } else {
452 unsafe { img.overlay_at(&i, scale * x as u32, scale * y as u32) };
453 }
454 continue;
455 }
456 let ctx = build.block.wants_context().then(|| {
457 let pctx = PositionContext {
458 position: GridPos(x, y),
459 width: self.width,
460 height: self.height,
461 };
462 RenderingContext {
463 cross: self
464 .cross(j)
465 .map(|b| b.and_then(|b| Some((b.get_block()?, b.get_rotation()?)))),
466 corners: Default::default(),
467 position: pctx,
468 }
469 });
470 unsafe {
471 img.as_mut().overlay_at(
472 &tile.build_image(ctx.as_ref(), scale),
473 scale * x as u32,
474 scale * y as u32,
475 )
476 };
477 }
478 }
479 }
480 for y in 0..self.height {
482 for x in 0..self.width {
483 let j = x + self.width * y;
484 let tile = unsafe { self.tiles.get_unchecked(j) };
485 let y = self.height - y - 1;
486
487 macro_rules! f {
488 ($($x: literal)+) => { paste::paste!{
489 ([$(load!([<rune _ overlay $x>] ),)+], [$(load!([<rune _ overlay _ crux $x>] ),)+])
490 }};
491 }
492 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];
493
494 if tile.has_ore() {
495 match tile.ore {
496 Type::RuneOverlay => unsafe {
497 img.overlay_at(
498 &RUNES.0[tile.nd[2] as usize][scale as usize],
499 scale * x as u32,
500 scale * y as u32,
501 );
502 },
503 Type::RuneOverlayCrux => unsafe {
504 img.overlay_at(
505 &RUNES.1[tile.nd[2] as usize][scale as usize],
506 scale * x as u32,
507 scale * y as u32,
508 );
509 },
510 Type::CharacterOverlay | Type::CharacterOverlayWhite => {
511 macro_rules! f {
512 ($($x: literal)+) => { paste::paste!{
513 [$(load!([<character _ overlay $x>] ),)+]
514 }};
515 }
516
517 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];
518 unsafe {
519 img.overlay_at(
520 LETTERS[(tile.nd[2] & 0x3f) as usize][scale as usize]
521 .mapped(image::Cow::Ref)
522 .rotate(4 - (tile.nd[2] >> 6)),
523 scale * x as u32,
524 scale * y as u32,
525 )
526 };
527 }
528 ore => unsafe {
529 img.overlay_at(&table(ore, scale), scale * x as u32, scale * y as u32);
530 },
531 }
532 }
533 }
534 }
535 for entity in &self.entities {
537 let (x, y) = (
539 entity.state.position.0 as u32,
540 self.height as u32 - entity.state.position.1 as u32 - 1,
541 );
542 if x < 10 || x as usize > self.width - 10 || y < 10 || y as usize > self.height - 10 {
543 continue;
544 }
545 unsafe {
546 img.as_mut()
547 .overlay_at(&entity.draw(scale), scale * x, scale * y)
548 };
549 }
550 img
551 }
552}
553
554#[test]
555fn all_blocks() {
556 use crate::block::content::Type;
557 use crate::content::Content;
558 for t in 19..Type::WorldMessage as u16 {
559 let t = Type::try_from(t).unwrap();
560 if matches!(t, |Type::Empty| Type::SlagCentrifuge
561 | Type::HeatReactor
562 | Type::LegacyMechPad
563 | Type::LegacyUnitFactory
564 | Type::LegacyUnitFactoryAir
565 | Type::LegacyUnitFactoryGround
566 | Type::CommandCenter)
567 {
568 continue;
569 }
570 let name = t.get_name();
571 let t = crate::block::BLOCK_REGISTRY.get(name).unwrap();
572 let _ = t.image(
573 None,
574 Some(&RenderingContext {
575 corners: [None; 4],
576 cross: [None; 4],
577 position: PositionContext {
578 position: GridPos(0, 0),
579 width: 5,
580 height: 5,
581 },
582 }),
583 Rotation::Up,
584 Scale::Quarter,
585 );
586 }
587}
588
589pub fn draw_units(
590 map: &mut crate::data::map::MapReader,
591 mut img: Image<&mut [u8], 3>,
592 size: (u16, u16),
593) -> Result<(), super::map::ReadError> {
594 use std::ops::CoroutineState::*;
595 let scale = if size.0 + size.1 < 2000 {
596 Scale::Quarter
597 } else {
598 Scale::Eigth
599 };
600
601 let mut co = map.entities()?;
602 let n = match Pin::new(&mut co).resume(()) {
603 Yielded(crate::data::map::EntityData::Length(x)) => x,
604 Complete(Err(e)) => return Err(e),
605 _ => unreachable!(),
606 };
607 'out: {
608 for _ in 0..n {
609 match Pin::new(&mut co).resume(()) {
610 Yielded(crate::data::map::EntityData::Data(entity)) => {
611 let (x, y) = (
613 entity.state.position.0 as u32,
614 size.1 as u32 - entity.state.position.1 as u32 - 1,
615 );
616 if x < 10
617 || x as usize > size.0 as usize - 10
618 || y < 10
619 || y as usize > size.1 as usize - 10
620 {
621 continue;
622 }
623 unsafe {
624 img.as_mut()
625 .overlay_at(&entity.draw(scale), scale * x, scale * y)
626 };
627 }
628 Complete(Err(e)) => return Err(e),
629 Complete(Ok(())) => break 'out,
630 x => unreachable!("{x:?}"),
631 }
632 }
633 match Pin::new(&mut co).resume(()) {
634 Complete(Ok(())) => (),
635 _ => unreachable!(),
636 };
637 }
638 Ok(())
639}
640
641pub fn draw_map_single(
647 map: &mut crate::data::map::MapReader,
648 r: Registrar,
649) -> Result<(Image<Box<[u8]>, 3>, (u16, u16)), super::map::ReadError> {
650 use std::ops::CoroutineState::*;
651 let mut co = map.thin_map(r)?;
652 let (w, h) = match Pin::new(&mut co).resume(()) {
653 Yielded(ThinMapData::Init { width, height }) => (width, height),
654 Complete(Err(x)) => return Err(x),
655 _ => unreachable!(),
656 };
657 let scale = if w + h < 2000 {
658 Scale::Quarter
659 } else {
660 Scale::Eigth
661 };
662 let mut img = uninit::Image::<_, 3>::new(
663 (scale * w as u32).try_into().unwrap(),
664 (scale * h as u32).try_into().unwrap(),
665 );
666 use crate::data::map::table;
667 for y in 0..h {
669 for x in 0..w {
670 let (floor, ore) = match Pin::new(&mut co).resume(()) {
671 Yielded(ThinMapData::Tile { floor, ore }) => (floor, ore),
672 Complete(Err(x)) => return Err(x),
673 _ => unreachable!(),
674 };
675 let y = h - y - 1;
676 unsafe { img.overlay_at(&table(floor, scale), scale * x as u32, scale * y as u32) };
678 if ore != Type::Air {
679 unsafe {
680 img.overlay_at(&table(ore, scale), scale * x as u32, scale * y as u32);
681 }
682 }
683 }
684 }
685 let mut img = unsafe { img.assume_init() }.boxed();
686 let mut i = 0;
687 while i < (w as usize * h as usize) {
688 let mut draw = |i, r, b: &'static crate::block::Block| {
689 let x = i % w as usize;
690 let y = i / w as usize;
691 let y = h as usize - y - 1;
692 let s = b.get_size();
693 let x = x
694 - (match s {
695 1 | 2 => 0,
696 3 | 4 => 1,
697 5 | 6 => 2,
698 7 | 8 => 3,
699 9 => 4,
700 _ => unsafe { std::hint::unreachable_unchecked() },
702 }) as usize;
703 let y = y
704 - (match s {
705 1 => 0,
706 2 | 3 => 1,
707 4 | 5 => 2,
708 6 | 7 => 3,
709 8 | 9 => 4,
710 _ => unsafe { std::hint::unreachable_unchecked() },
712 }) as usize;
713 let ctx = b.wants_context().then(|| {
714 let pctx = PositionContext {
715 position: GridPos(x, y),
716 width: w as usize,
717 height: h as usize,
718 };
719 RenderingContext {
720 corners: [None; 4],
721 cross: [None; 4], position: pctx,
723 }
724 });
725 unsafe {
726 img.as_mut().overlay_at(
727 &b.image(None, ctx.as_ref(), r, scale),
728 scale * x as u32,
729 scale * y as u32,
730 )
731 };
732 };
733 match Pin::new(&mut co).resume(()) {
734 Yielded(ThinMapData::Bloc(ThinBloc::Many(None, n))) => {
735 i += n as usize;
736 }
737 Yielded(ThinMapData::Bloc(ThinBloc::Build(r, bloc, _))) => {
738 draw(i, r, bloc);
739 }
740 Yielded(ThinMapData::Bloc(ThinBloc::Many(Some(bloc), n))) => {
741 for i in i..=i + n as usize {
742 draw(i, Rotation::Up, bloc);
743 }
744 i += n as usize;
745 }
746 Complete(Err(x)) => return Err(x),
747 x => unreachable!("{x:?}"),
748 }
749 i += 1;
750 }
751 match Pin::new(&mut co).resume(()) {
752 Complete(Ok(())) => (),
753 f => unreachable!("{f:?}"),
754 };
755
756 Ok((img, (w, h)))
757}
758
759pub fn draw_map_simple(
762 map: &mut crate::data::map::MapReader,
763 r: Registrar,
764) -> Result<(Image<Box<[u8]>, 3>, (u16, u16)), super::map::ReadError> {
765 use std::ops::CoroutineState::*;
766 let mut co = map.thin_map(r)?;
767 let (w, h) = match Pin::new(&mut co).resume(()) {
768 Yielded(ThinMapData::Init { width, height }) => (width, height),
769 Complete(Err(x)) => return Err(x),
770 _ => unreachable!(),
771 };
772 let mut img = uninit::Image::<u8, 3>::new(
773 (w as u32).try_into().unwrap(),
774 (h as u32).try_into().unwrap(),
775 );
776 for y in 0..h {
778 for x in 0..w {
779 let (floor, ore) = match Pin::new(&mut co).resume(()) {
780 Yielded(ThinMapData::Tile { floor, ore }) => (floor, ore),
781 Complete(Err(x)) => return Err(x),
782 _ => unreachable!(),
783 };
784 let t = (ore != Type::Air).then_some(ore).unwrap_or(floor);
785 let y = h - y - 1;
786 let i1 = img.at(x as u32, y as u32) as usize;
787 unsafe {
788 img.slice(i1..i1 + 3)
789 .write_copy_of_slice(&BLOCK2COLOR[t as u16 as usize][..])
790 };
791 }
792 }
793 let mut img = unsafe { img.assume_init() }.boxed();
794 let mut i = 0;
795 while i < (w as usize * h as usize) {
796 let mut draw = |i, b: &'static crate::block::Block, team: Team| {
797 let x = i % w as usize;
798 let y = i / w as usize;
799 let y = h as usize - y - 1;
800 let s = b.get_size();
801 let x = x
802 - (match s {
803 1 | 2 => 0,
804 3 | 4 => 1,
805 5 | 6 => 2,
806 7 | 8 => 3,
807 9 => 4,
808 _ => unsafe { std::hint::unreachable_unchecked() },
810 }) as usize;
811 let y = y
812 - (match s {
813 1 => 0,
814 2 | 3 => 1,
815 4 | 5 => 2,
816 6 | 7 => 3,
817 8 | 9 => 4,
818 _ => unsafe { std::hint::unreachable_unchecked() },
820 }) as usize;
821 for x in x..(x as usize + s as usize).min(w as usize) {
822 for y in y..(y as usize + s as usize).min(h as usize) {
823 unsafe {
824 img.set_pixel(x as u32, y as u32, &<[u8; 3]>::from(team.color()));
825 }
826 }
827 }
828 };
829 match Pin::new(&mut co).resume(()) {
830 Yielded(ThinMapData::Bloc(ThinBloc::Many(None, n))) => {
831 i += n as usize;
832 }
833 Yielded(ThinMapData::Bloc(ThinBloc::Build(_, bloc, t))) => {
834 draw(i, bloc, t);
835 }
836 Yielded(ThinMapData::Bloc(ThinBloc::Many(Some(bloc), n))) => {
837 for i in i..=i + n as usize {
838 draw(i, bloc, Team::DERELICT);
839 }
840 i += n as usize;
841 }
842 Complete(Err(x)) => return Err(x),
843 x => unreachable!("{x:?}"),
844 }
845 i += 1;
846 }
847 match Pin::new(&mut co).resume(()) {
848 Complete(Ok(())) => (),
849 f => unreachable!("{f:?}"),
850 };
851
852 Ok((img, (w, h)))
853}