1use crate::prelude::{
5 string_to_cp437, to_cp437, BTerm, CharacterTranslationMode, ColoredTextSpans, Console,
6 DrawBatch, FontCharType, TextAlign, Tile, XpLayer,
7};
8use bracket_color::prelude::*;
9use bracket_geometry::prelude::{Point, Rect};
10use std::any::Any;
11
12pub struct VirtualConsole {
13 pub width: u32,
14 pub height: u32,
15
16 pub tiles: Vec<Tile>,
17
18 pub extra_clipping: Option<Rect>,
19 pub translation: CharacterTranslationMode,
20}
21
22impl VirtualConsole {
23 pub fn new(dimensions: Point) -> Self {
25 let num_tiles: usize = (dimensions.x * dimensions.y) as usize;
26 let mut console = VirtualConsole {
27 width: dimensions.x as u32,
28 height: dimensions.y as u32,
29 tiles: Vec::with_capacity(num_tiles),
30 extra_clipping: None,
31 translation: CharacterTranslationMode::Codepage437,
32 };
33 for _ in 0..num_tiles {
34 console.tiles.push(Tile {
35 glyph: 0,
36 fg: RGBA::from_f32(1.0, 1.0, 1.0, 1.0),
37 bg: RGBA::from_f32(0.0, 0.0, 0.0, 1.0),
38 });
39 }
40 console
41 }
42
43 pub fn from_text(text: &str, width: usize) -> Self {
46 let raw_lines = text.split('\n');
47 let mut lines: Vec<String> = Vec::new();
48 for line in raw_lines {
49 let mut newline: String = String::from("");
50
51 line.chars().for_each(|c| {
52 newline.push(c);
53 if newline.len() > width {
54 lines.push(newline.clone());
55 newline.clear();
56 }
57 });
58 lines.push(newline.clone());
59 }
60
61 let num_tiles: usize = width * lines.len();
62 let mut console = VirtualConsole {
63 width: width as u32,
64 height: lines.len() as u32,
65 tiles: Vec::with_capacity(num_tiles),
66 extra_clipping: None,
67 translation: CharacterTranslationMode::Codepage437,
68 };
69 for _ in 0..num_tiles {
72 console.tiles.push(Tile {
73 glyph: 0,
74 fg: RGBA::from_f32(1.0, 1.0, 1.0, 1.0),
75 bg: RGBA::from_f32(0.0, 0.0, 0.0, 1.0),
76 });
77 }
78
79 for (i, line) in lines.iter().enumerate() {
80 console.print(0, i as i32, line);
81 }
82
83 console
84 }
85
86 pub fn print_sub_rect(&self, source: Rect, dest: Rect, target: &mut BTerm) {
89 target.set_clipping(Some(dest));
90 for y in dest.y1..dest.y2 {
91 let source_y = y + source.y1 - dest.y1;
92 for x in dest.x1..dest.x2 {
93 let source_x = x + source.x1 - dest.x1;
94 if let Some(idx) = self.try_at(source_x, source_y) {
95 let t = self.tiles[idx];
96 if t.glyph > 0 {
97 target.set(x, y, t.fg, t.bg, t.glyph);
98 }
99 }
100 }
101 }
102 target.set_clipping(None);
103 }
104
105 pub fn batch_sub_rect(&self, source: Rect, dest: Rect, target: &mut DrawBatch) {
108 target.set_clipping(Some(dest));
109 for y in dest.y1..dest.y2 {
110 let source_y = y + source.y1 - dest.y1;
111 for x in dest.x1..dest.x2 {
112 let source_x = x + source.x1 - dest.x1;
113 if let Some(idx) = self.try_at(source_x, source_y) {
114 let t = self.tiles[idx];
115 if t.glyph > 0 {
116 target.set(Point::new(x, y), ColorPair::new(t.fg, t.bg), t.glyph);
117 }
118 }
119 }
120 }
121 target.set_clipping(None);
122 }
123}
124
125impl Console for VirtualConsole {
126 fn get_char_size(&self) -> (u32, u32) {
127 (self.width, self.height)
128 }
129
130 fn resize_pixels(&mut self, _width: u32, _height: u32) {
131 }
133
134 fn at(&self, x: i32, y: i32) -> usize {
136 (((self.height - 1 - y as u32) * self.width) + x as u32) as usize
137 }
138
139 fn cls(&mut self) {
141 for tile in &mut self.tiles {
142 tile.glyph = 32;
143 tile.fg = RGBA::from_f32(1.0, 1.0, 1.0, 1.0);
144 tile.bg = RGBA::from_f32(0.0, 0.0, 0.0, 1.0);
145 }
146 }
147
148 fn cls_bg(&mut self, background: RGBA) {
150 for tile in &mut self.tiles {
151 tile.glyph = 32;
152 tile.fg = RGBA::from_f32(1.0, 1.0, 1.0, 1.0);
153 tile.bg = background;
154 }
155 }
156
157 fn print(&mut self, mut x: i32, y: i32, output: &str) {
159 let bytes = match self.translation {
160 CharacterTranslationMode::Codepage437 => string_to_cp437(output),
161 CharacterTranslationMode::Unicode => {
162 output.chars().map(|c| c as FontCharType).collect()
163 }
164 };
165 for glyph in bytes {
166 if let Some(idx) = self.try_at(x, y) {
167 self.tiles[idx].glyph = glyph;
168 }
169 x += 1;
170 }
171 }
172
173 fn print_color(&mut self, mut x: i32, y: i32, fg: RGBA, bg: RGBA, output: &str) {
175 let bytes = match self.translation {
176 CharacterTranslationMode::Codepage437 => string_to_cp437(output),
177 CharacterTranslationMode::Unicode => {
178 output.chars().map(|c| c as FontCharType).collect()
179 }
180 };
181 for glyph in bytes {
182 if let Some(idx) = self.try_at(x, y) {
183 self.tiles[idx].glyph = glyph;
184 self.tiles[idx].bg = bg;
185 self.tiles[idx].fg = fg;
186 }
187 x += 1;
188 }
189 }
190
191 fn set(&mut self, x: i32, y: i32, fg: RGBA, bg: RGBA, glyph: FontCharType) {
193 if let Some(idx) = self.try_at(x, y) {
194 self.tiles[idx].glyph = glyph;
195 self.tiles[idx].fg = fg;
196 self.tiles[idx].bg = bg;
197 }
198 }
199
200 fn set_bg(&mut self, x: i32, y: i32, bg: RGBA) {
202 if let Some(idx) = self.try_at(x, y) {
203 self.tiles[idx].bg = bg;
204 }
205 }
206
207 fn draw_box(&mut self, sx: i32, sy: i32, width: i32, height: i32, fg: RGBA, bg: RGBA) {
209 crate::prelude::draw_box(self, sx, sy, width, height, fg, bg);
210 }
211
212 fn draw_hollow_box(&mut self, sx: i32, sy: i32, width: i32, height: i32, fg: RGBA, bg: RGBA) {
214 crate::prelude::draw_hollow_box(self, sx, sy, width, height, fg, bg);
215 }
216
217 fn draw_box_double(&mut self, sx: i32, sy: i32, width: i32, height: i32, fg: RGBA, bg: RGBA) {
219 crate::prelude::draw_box_double(self, sx, sy, width, height, fg, bg);
220 }
221
222 fn draw_hollow_box_double(
224 &mut self,
225 sx: i32,
226 sy: i32,
227 width: i32,
228 height: i32,
229 fg: RGBA,
230 bg: RGBA,
231 ) {
232 crate::prelude::draw_hollow_box_double(self, sx, sy, width, height, fg, bg);
233 }
234
235 fn fill_region(&mut self, target: Rect, glyph: FontCharType, fg: RGBA, bg: RGBA) {
237 target.for_each(|point| {
238 self.set(point.x, point.y, fg, bg, glyph);
239 });
240 }
241
242 fn draw_bar_horizontal(
244 &mut self,
245 sx: i32,
246 sy: i32,
247 width: i32,
248 n: i32,
249 max: i32,
250 fg: RGBA,
251 bg: RGBA,
252 ) {
253 crate::prelude::draw_bar_horizontal(self, sx, sy, width, n, max, fg, bg);
254 }
255
256 fn draw_bar_vertical(
258 &mut self,
259 sx: i32,
260 sy: i32,
261 height: i32,
262 n: i32,
263 max: i32,
264 fg: RGBA,
265 bg: RGBA,
266 ) {
267 crate::prelude::draw_bar_vertical(self, sx, sy, height, n, max, fg, bg);
268 }
269
270 fn print_centered(&mut self, y: i32, text: &str) {
272 self.print(
273 (self.width as i32 / 2) - (text.to_string().len() as i32 / 2),
274 y,
275 text,
276 );
277 }
278
279 fn print_color_centered(&mut self, y: i32, fg: RGBA, bg: RGBA, text: &str) {
281 self.print_color(
282 (self.width as i32 / 2) - (text.to_string().len() as i32 / 2),
283 y,
284 fg,
285 bg,
286 text,
287 );
288 }
289
290 fn print_centered_at(&mut self, x: i32, y: i32, text: &str) {
292 self.print(x - (text.to_string().len() as i32 / 2), y, text);
293 }
294
295 fn print_color_centered_at(&mut self, x: i32, y: i32, fg: RGBA, bg: RGBA, text: &str) {
297 self.print_color(x - (text.to_string().len() as i32 / 2), y, fg, bg, text);
298 }
299
300 fn print_right(&mut self, x: i32, y: i32, text: &str) {
302 let len = text.len() as i32;
303 let actual_x = x - len;
304 self.print(actual_x, y, text);
305 }
306
307 fn print_color_right(&mut self, x: i32, y: i32, fg: RGBA, bg: RGBA, text: &str) {
309 let len = text.len() as i32;
310 let actual_x = x - len;
311 self.print_color(actual_x, y, fg, bg, text);
312 }
313
314 fn printer(
319 &mut self,
320 x: i32,
321 y: i32,
322 output: &str,
323 align: TextAlign,
324 background: Option<RGBA>,
325 ) {
326 let bg = if let Some(bg) = background {
327 bg
328 } else {
329 RGBA::from_f32(0.0, 0.0, 0.0, 1.0)
330 };
331
332 let split_text = ColoredTextSpans::new(output);
333
334 let mut tx = match align {
335 TextAlign::Left => x,
336 TextAlign::Center => x - (split_text.length as i32 / 2),
337 TextAlign::Right => x - split_text.length as i32,
338 };
339 for span in split_text.spans.iter() {
340 let fg = span.0;
341 for ch in span.1.chars() {
342 self.set(
343 tx,
344 y,
345 fg,
346 bg,
347 match self.translation {
348 CharacterTranslationMode::Codepage437 => to_cp437(ch),
349 CharacterTranslationMode::Unicode => ch as FontCharType,
350 },
351 );
352 tx += 1;
353 }
354 }
355 }
356
357 fn to_xp_layer(&self) -> XpLayer {
359 let mut layer = XpLayer::new(self.width as usize, self.height as usize);
360
361 for y in 0..self.height {
362 for x in 0..self.width {
363 let cell = layer.get_mut(x as usize, y as usize).unwrap();
364 let idx = self.at(x as i32, y as i32);
365 cell.ch = u32::from(self.tiles[idx].glyph);
366 cell.fg = self.tiles[idx].fg.into();
367 cell.bg = self.tiles[idx].bg.into();
368 }
369 }
370
371 layer
372 }
373
374 fn set_offset(&mut self, _x: f32, _y: f32) {
378 panic!("Unsupported on virtual consoles.");
379 }
380
381 fn set_scale(&mut self, _scale: f32, _center_x: i32, _center_y: i32) {
382 panic!("Unsupported on virtual consoles.");
383 }
384
385 fn get_scale(&self) -> (f32, i32, i32) {
386 (1.0, 0, 0)
387 }
388
389 fn as_any(&self) -> &dyn Any {
390 self
391 }
392
393 fn as_any_mut(&mut self) -> &mut dyn Any {
394 self
395 }
396
397 fn set_clipping(&mut self, clipping: Option<Rect>) {
400 self.extra_clipping = clipping;
401 }
402
403 fn get_clipping(&self) -> Option<Rect> {
405 self.extra_clipping
406 }
407
408 fn set_all_fg_alpha(&mut self, alpha: f32) {
410 self.tiles.iter_mut().for_each(|t| t.fg.a = alpha);
411 }
412
413 fn set_all_bg_alpha(&mut self, alpha: f32) {
415 self.tiles.iter_mut().for_each(|t| t.bg.a = alpha);
416 }
417
418 fn set_all_alpha(&mut self, fg: f32, bg: f32) {
420 self.tiles.iter_mut().for_each(|t| {
421 t.fg.a = fg;
422 t.bg.a = bg;
423 });
424 }
425
426 fn set_translation_mode(&mut self, mode: CharacterTranslationMode) {
428 self.translation = mode;
429 }
430
431 fn set_char_size(&mut self, _width: u32, _height: u32) {
433 panic!("Not implemented.");
434 }
435
436 fn clear_dirty(&mut self) {}
438}