use crate::tui::ecs::components::TermColor;
use crate::tui::ecs::world::{LABEL, POSITION, SPRITE, TILEMAP, World as TuiWorld};
#[derive(Clone)]
pub struct GridCell {
pub character: char,
pub foreground: TermColor,
pub background: TermColor,
}
impl Default for GridCell {
fn default() -> Self {
Self {
character: ' ',
foreground: TermColor::White,
background: TermColor::Black,
}
}
}
pub struct CellGrid {
pub columns: u16,
pub rows: u16,
pub cells: Vec<GridCell>,
}
impl CellGrid {
pub fn new(columns: u16, rows: u16) -> Self {
let size = columns as usize * rows as usize;
Self {
columns,
rows,
cells: vec![GridCell::default(); size],
}
}
pub fn clear(&mut self) {
for cell in &mut self.cells {
*cell = GridCell::default();
}
}
pub fn set(&mut self, column: i32, row: i32, cell: GridCell) {
if column >= 0 && column < self.columns as i32 && row >= 0 && row < self.rows as i32 {
let index = row as usize * self.columns as usize + column as usize;
self.cells[index] = cell;
}
}
pub fn get(&self, column: u16, row: u16) -> &GridCell {
let index = row as usize * self.columns as usize + column as usize;
&self.cells[index]
}
}
enum DrawableKind {
Sprite,
Label,
Tilemap,
}
pub fn build_grid_from_tui_world(tui_world: &TuiWorld, grid: &mut CellGrid) {
grid.clear();
let camera = tui_world.resources.camera;
let mut drawables: Vec<(i32, freecs::Entity, DrawableKind)> = Vec::new();
for entity in tui_world.query_entities(POSITION | SPRITE) {
let has_label = tui_world.get_label(entity).is_some();
let has_tilemap = tui_world.get_tilemap(entity).is_some();
if has_label || has_tilemap {
continue;
}
let visible = tui_world
.get_visibility(entity)
.is_none_or(|visibility| visibility.visible);
if !visible {
continue;
}
let z = tui_world.get_z_index(entity).map_or(0, |z_index| z_index.0);
drawables.push((z, entity, DrawableKind::Sprite));
}
for entity in tui_world.query_entities(POSITION | LABEL) {
let visible = tui_world
.get_visibility(entity)
.is_none_or(|visibility| visibility.visible);
if !visible {
continue;
}
let z = tui_world.get_z_index(entity).map_or(0, |z_index| z_index.0);
drawables.push((z, entity, DrawableKind::Label));
}
for entity in tui_world.query_entities(POSITION | TILEMAP) {
let visible = tui_world
.get_visibility(entity)
.is_none_or(|visibility| visibility.visible);
if !visible {
continue;
}
let z = tui_world.get_z_index(entity).map_or(0, |z_index| z_index.0);
drawables.push((z, entity, DrawableKind::Tilemap));
}
drawables.sort_by_key(|(z, _, _)| *z);
for (_, entity, kind) in &drawables {
let Some(position) = tui_world.get_position(*entity) else {
continue;
};
let screen_column = (position.column - camera.offset_column).round() as i32;
let screen_row = (position.row - camera.offset_row).round() as i32;
match kind {
DrawableKind::Sprite => {
let Some(sprite) = tui_world.get_sprite(*entity) else {
continue;
};
grid.set(
screen_column,
screen_row,
GridCell {
character: sprite.character,
foreground: sprite.foreground,
background: sprite.background,
},
);
}
DrawableKind::Label => {
let Some(label) = tui_world.get_label(*entity) else {
continue;
};
for (char_index, character) in label.text.chars().enumerate() {
grid.set(
screen_column + char_index as i32,
screen_row,
GridCell {
character,
foreground: label.foreground,
background: label.background,
},
);
}
}
DrawableKind::Tilemap => {
let Some(tilemap) = tui_world.get_tilemap(*entity) else {
continue;
};
for tile_row in 0..tilemap.height {
for tile_column in 0..tilemap.width {
let cell = &tilemap.cells[tile_row * tilemap.width + tile_column];
if cell.character == '\0' {
continue;
}
grid.set(
screen_column + tile_column as i32,
screen_row + tile_row as i32,
GridCell {
character: cell.character,
foreground: cell.foreground,
background: cell.background,
},
);
}
}
}
}
}
}