bracket_terminal/consoles/text/
multi_tile_sprite.rs

1use crate::prelude::{string_to_cp437, BTerm, DrawBatch, FontCharType, Tile, XpFile};
2use bracket_color::prelude::{ColorPair, RGBA};
3use bracket_geometry::prelude::Point;
4
5/// Represents a sprite consisting of multiple glyphs/colors, occupying multiple console locations.
6pub struct MultiTileSprite {
7    content: Vec<Tile>,
8    dimensions: Point,
9}
10
11impl MultiTileSprite {
12    /// Generates a sprite from an input string, divided into width x height sizes.
13    pub fn from_string<S: ToString, T>(content: S, width: T, height: T) -> Self
14    where
15        T: Into<i32>,
16    {
17        let w: i32 = width.into();
18        let h: i32 = height.into();
19        let content_s = content.to_string();
20
21        let bytes = string_to_cp437(content_s);
22        let tiles = bytes
23            .into_iter()
24            .map(|glyph| Tile {
25                glyph,
26                fg: RGBA::from_f32(1.0, 1.0, 1.0, 1.0),
27                bg: RGBA::from_f32(0.0, 0.0, 0.0, 1.0),
28            })
29            .collect();
30
31        Self {
32            content: tiles,
33            dimensions: Point::new(w, h),
34        }
35    }
36
37    /// Generates a sprite from an input string, divided into width x height sizes. Also provides foreground and background colors.
38    pub fn from_string_colored<S: ToString, T>(
39        content: S,
40        width: T,
41        height: T,
42        fg: &[RGBA],
43        bg: &[RGBA],
44    ) -> Self
45    where
46        T: Into<i32>,
47    {
48        let w: i32 = width.into();
49        let h: i32 = height.into();
50        let content_s = content.to_string();
51
52        let bytes = string_to_cp437(content_s);
53        let tiles = bytes
54            .into_iter()
55            .enumerate()
56            .map(|(i, glyph)| Tile {
57                glyph,
58                fg: fg[i],
59                bg: bg[i],
60            })
61            .collect();
62
63        Self {
64            content: tiles,
65            dimensions: Point::new(w, h),
66        }
67    }
68
69    /// Import a sprite from an XP Rex Paint file.
70    pub fn from_xp(rex: &XpFile) -> Self {
71        let dimensions = Point::new(rex.layers[0].width, rex.layers[0].height);
72        let mut tiles: Vec<Tile> = vec![
73            Tile {
74                glyph: 0,
75                fg: RGBA::from_f32(1.0, 1.0, 1.0, 1.0),
76                bg: RGBA::from_f32(0.0, 0.0, 0.0, 1.0)
77            };
78            (dimensions.x * dimensions.y) as usize
79        ];
80
81        for layer in &rex.layers {
82            for y in 0..layer.height {
83                for x in 0..layer.width {
84                    let cell = layer.get(x, y).unwrap();
85                    if !cell.bg.is_transparent() {
86                        let idx = (y * (dimensions.x as usize)) + (x as usize);
87                        tiles[idx].glyph = cell.ch as FontCharType;
88                        tiles[idx].fg = cell.fg.into();
89                        tiles[idx].bg = cell.bg.into();
90                    }
91                }
92            }
93        }
94
95        Self {
96            content: tiles,
97            dimensions,
98        }
99    }
100
101    /// Directly renders a sprite to an BTerm context.
102    pub fn render(&self, context: &mut BTerm, position: Point) {
103        let mut x = 0;
104        let mut y = 0;
105        for tile in self.content.iter() {
106            x += 1;
107            context.set(x + position.x, y + position.y, tile.fg, tile.bg, tile.glyph);
108            if x >= self.dimensions.x {
109                x = 0;
110                y += 1;
111            }
112        }
113    }
114
115    /// Appends draw-calls to a batch to render a multi-tile sprite.
116    pub fn add_to_batch(&self, batch: &mut DrawBatch, position: Point) {
117        let mut pos = Point::zero();
118        for tile in self.content.iter() {
119            pos.x += 1;
120            batch.set(pos + position, ColorPair::new(tile.fg, tile.bg), tile.glyph);
121            if pos.x >= self.dimensions.x {
122                pos.x = 0;
123                pos.y += 1;
124            }
125        }
126    }
127}