use ::rltk::{Point, Rltk, RGB}; use ::specs::prelude::*; use crate::{Hidden, Map, Position, Renderable, TileType}; const SHOW_BOUNDARIES: bool = false; pub fn get_screen_bounds(ecs: &World, ctx: &mut Rltk) -> (i32, i32, i32, i32) { let player_pos = ecs.fetch::(); let (x_chars, y_chars) = ctx.get_char_size(); let center_x = (x_chars / 2) as i32; let center_y = (y_chars / 2) as i32; let min_x = player_pos.x - center_x; let max_x = min_x + x_chars as i32; let min_y = player_pos.y - center_y; let max_y = min_y + y_chars as i32; (min_x, max_x, min_y, max_y) } pub fn render_camera(ecs: &World, ctx: &mut Rltk) { let map = ecs.fetch::(); let (min_x, max_x, min_y, max_y) = get_screen_bounds(ecs, ctx); // Render the Map let map_width = map.width - 1; let map_height = map.height - 1; let mut y = 0; #[allow(clippy::explicit_counter_loop)] for ty in min_y..max_y { let mut x = 0; for tx in min_x..max_x { if tx > 0 && tx < map_width && ty > 0 && ty < map_height { let idx = map.xy_idx(tx, ty); if map.revealed_tiles[idx] { let (glyph, fg, bg) = get_tile_glyph(idx, &*map); ctx.set(x, y, fg, bg, glyph); } } else if SHOW_BOUNDARIES { ctx.set( x, y, RGB::named(rltk::GRAY), RGB::named(rltk::BLACK), rltk::to_cp437('·'), ); } x += 1; } y += 1; } // Render entities let positions = ecs.read_storage::(); let renderables = ecs.read_storage::(); let hidden = ecs.read_storage::(); let map = ecs.fetch::(); let mut data = (&positions, &renderables, !&hidden) .join() .collect::>(); data.sort_by(|&a, &b| b.1.render_order.cmp(&a.1.render_order)); for (pos, render, _hidden) in data.iter() { let idx = map.xy_idx(pos.x, pos.y); if map.visible_tiles[idx] { let entity_screen_x = pos.x - min_x; let entity_screen_y = pos.y - min_y; if entity_screen_x > 0 && entity_screen_x < map_width && entity_screen_y > 0 && entity_screen_y < map_height { ctx.set( entity_screen_x, entity_screen_y, render.fg, render.bg, render.glyph, ); } } } } pub fn render_debug_map(map: &Map, ctx: &mut Rltk) { let player_pos = Point::new(map.width / 2, map.height / 2); let (x_chars, y_chars) = ctx.get_char_size(); let center_x = (x_chars / 2) as i32; let center_y = (y_chars / 2) as i32; let min_x = player_pos.x - center_x; let max_x = min_x + x_chars as i32; let min_y = player_pos.y - center_y; let max_y = min_y + y_chars as i32; let map_width = map.width - 1; let map_height = map.height - 1; let mut y = 0; #[allow(clippy::explicit_counter_loop)] for ty in min_y..max_y { let mut x = 0; for tx in min_x..max_x { if tx > 0 && tx < map_width && ty > 0 && ty < map_height { let idx = map.xy_idx(tx, ty); if map.revealed_tiles[idx] { let (glyph, fg, bg) = get_tile_glyph(idx, &*map); ctx.set(x, y, fg, bg, glyph); } } else if SHOW_BOUNDARIES { ctx.set( x, y, RGB::named(rltk::GRAY), RGB::named(rltk::BLACK), rltk::to_cp437('·'), ); } x += 1; } y += 1; } } fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) { let mut bg = RGB::from_f32(0., 0., 0.); let (glyph, mut fg) = match map.tiles[idx] { TileType::Floor => (rltk::to_cp437('.'), RGB::from_f32(0., 0.5, 0.5)), TileType::WoodFloor => (rltk::to_cp437('.'), RGB::named(rltk::CHOCOLATE)), TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; (wall_glyph(&*map, x, y), RGB::from_f32(0., 1.0, 0.)) } TileType::DownStairs => (rltk::to_cp437('>'), RGB::from_f32(0., 1.0, 1.0)), TileType::Bridge => (rltk::to_cp437('.'), RGB::named(rltk::CHOCOLATE)), TileType::Road => (rltk::to_cp437('~'), RGB::named(rltk::GRAY)), TileType::Grass => (rltk::to_cp437('"'), RGB::named(rltk::GREEN)), TileType::ShallowWater => (rltk::to_cp437('≈'), RGB::named(rltk::CYAN)), TileType::DeepWater => (rltk::to_cp437('≈'), RGB::named(rltk::NAVY_BLUE)), TileType::Gravel => (rltk::to_cp437(';'), RGB::named(rltk::GRAY)), }; if map.bloodstains.contains(&idx) { bg = RGB::from_f32(0.75, 0., 0.); } if !map.visible_tiles[idx] { fg = fg.to_greyscale(); // Don't show bloodstains out of visual range bg = RGB::from_f32(0., 0., 0.); } (glyph, fg, bg) } fn wall_glyph(map: &Map, x: i32, y: i32) -> rltk::FontCharType { if x < 1 || x > map.width - 2 || y < 1 || y > map.height - 2 { return 35; } let mut mask = 0u8; if is_revealed_and_wall(map, x, y - 1) { mask += 1; } if is_revealed_and_wall(map, x, y + 1) { mask += 2; } if is_revealed_and_wall(map, x - 1, y) { mask += 4; } if is_revealed_and_wall(map, x + 1, y) { mask += 8; } match mask { 0 => 9, // Pillar because we can't see neighbors 1 => 186, // Wall only to the north 2 => 186, // Wall only to the south 3 => 186, // Wall to the north and south 4 => 205, // Wall only to the west 5 => 188, // Wall to the north and west 6 => 187, // Wall to the south and west 7 => 185, // Wall to the north, south, and west 8 => 205, // Wall only to the east 9 => 200, // Wall to the north and east 10 => 201, // Wall to the sound and east 11 => 204, // Wall to the north, south, and east 12 => 205, // Wall to the east and west 13 => 202, // Wall to the east, west, and south 14 => 203, // Wall to the east, west, and north 15 => 206, // ╬ Wall on all sides _ => 35, // We missed one? } } fn is_revealed_and_wall(map: &Map, x: i32, y: i32) -> bool { let idx = map.xy_idx(x, y); map.tiles[idx] == TileType::Wall && map.revealed_tiles[idx] }