From e475a91c19e447e3267dffdea309fb491b126c8c Mon Sep 17 00:00:00 2001 From: Timothy Warren Date: Tue, 4 Jan 2022 15:02:50 -0500 Subject: [PATCH] Add new theme for forest map --- src/camera.rs | 87 ++------------------------ src/map.rs | 2 + src/map/themes.rs | 121 +++++++++++++++++++++++++++++++++++++ src/map_builders.rs | 3 + src/map_builders/forest.rs | 26 ++++++++ src/map_builders/town.rs | 6 +- 6 files changed, 160 insertions(+), 85 deletions(-) create mode 100644 src/map/themes.rs create mode 100644 src/map_builders/forest.rs diff --git a/src/camera.rs b/src/camera.rs index a790317..b82ab72 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,7 +1,8 @@ use ::rltk::{Point, Rltk, RGB}; use ::specs::prelude::*; -use crate::{Hidden, Map, Position, Renderable, TileType}; +use crate::map::tile_glyph; +use crate::{Hidden, Map, Position, Renderable}; const SHOW_BOUNDARIES: bool = false; @@ -37,7 +38,7 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) { 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); + let (glyph, fg, bg) = tile_glyph(idx, &*map); ctx.set(x, y, fg, bg, glyph); } } else if SHOW_BOUNDARIES { @@ -109,7 +110,7 @@ pub fn render_debug_map(map: &Map, ctx: &mut Rltk) { 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); + let (glyph, fg, bg) = tile_glyph(idx, &*map); ctx.set(x, y, fg, bg, glyph); } } else if SHOW_BOUNDARIES { @@ -126,83 +127,3 @@ pub fn render_debug_map(map: &Map, ctx: &mut Rltk) { 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] -} diff --git a/src/map.rs b/src/map.rs index d1b8731..fec0388 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,3 +1,4 @@ +mod themes; mod tiletype; use std::collections::HashSet; @@ -5,6 +6,7 @@ use std::collections::HashSet; use ::rltk::{Algorithm2D, BaseMap, Point, SmallVec}; use ::serde::{Deserialize, Serialize}; use ::specs::prelude::*; +pub use themes::*; pub use tiletype::{tile_opaque, tile_walkable, TileType}; use crate::map::tiletype::tile_cost; diff --git a/src/map/themes.rs b/src/map/themes.rs new file mode 100644 index 0000000..3c31950 --- /dev/null +++ b/src/map/themes.rs @@ -0,0 +1,121 @@ +use ::rltk::{FontCharType, RGB}; + +use super::{Map, TileType}; + +pub fn tile_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { + let (glyph, mut fg, mut bg) = match map.depth { + 2 => get_forest_glyph(idx, map), + _ => get_tile_glyph_default(idx, map), + }; + + if map.bloodstains.contains(&idx) { + bg = RGB::from_f32(0.7, 0., 0.); + } + + if !map.visible_tiles[idx] { + fg = fg.to_greyscale(); + bg = RGB::from_f32(0., 0., 0.); + } + + (glyph, fg, bg) +} + +fn get_forest_glyph(idx: usize, map: &Map) -> (FontCharType, RGB, RGB) { + use rltk::to_cp437; + + let bg = RGB::from_f32(0., 0., 0.); + + let (glyph, fg) = match map.tiles[idx] { + TileType::Wall => (to_cp437('♣'), RGB::from_f32(0.0, 0.6, 0.0)), + TileType::Bridge => (to_cp437('.'), RGB::named(rltk::CHOCOLATE)), + TileType::Road => (to_cp437('≡'), RGB::named(rltk::YELLOW)), + TileType::Grass => (to_cp437('"'), RGB::named(rltk::GREEN)), + TileType::ShallowWater => (to_cp437('~'), RGB::named(rltk::CYAN)), + TileType::DeepWater => (to_cp437('~'), RGB::named(rltk::BLUE)), + TileType::Gravel => (to_cp437(';'), RGB::from_f32(0.5, 0.5, 0.5)), + TileType::DownStairs => (to_cp437('>'), RGB::from_f32(0., 1.0, 1.0)), + _ => (to_cp437('"'), RGB::from_f32(0.0, 0.6, 0.0)), + }; + + (glyph, fg, bg) +} + +fn get_tile_glyph_default(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) -> 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] +} diff --git a/src/map_builders.rs b/src/map_builders.rs index 55f83d0..a731e7c 100644 --- a/src/map_builders.rs +++ b/src/map_builders.rs @@ -8,6 +8,7 @@ mod distant_exit; mod dla; mod door_placement; mod drunkard; +mod forest; mod maze; mod prefab_builder; mod room_based_spawner; @@ -39,6 +40,7 @@ use distant_exit::DistantExit; use dla::DLABuilder; use door_placement::DoorPlacement; use drunkard::DrunkardsWalkBuilder; +use forest::forest_builder; use maze::MazeBuilder; use prefab_builder::PrefabBuilder; use room_based_spawner::RoomBasedSpawner; @@ -342,6 +344,7 @@ pub fn level_builder( rltk::console::log(format!("Depth: {}", new_depth)); match new_depth { 1 => town_builder(new_depth, rng, width, height), + 2 => forest_builder(new_depth, rng, width, height), _ => random_builder(new_depth, rng, width, height), } } diff --git a/src/map_builders/forest.rs b/src/map_builders/forest.rs new file mode 100644 index 0000000..b4ddc1f --- /dev/null +++ b/src/map_builders/forest.rs @@ -0,0 +1,26 @@ +use ::rltk::RandomNumberGenerator; + +use super::{ + AreaStartingPosition, BuilderChain, CellularAutomataBuilder, CullUnreachable, DistantExit, + VoronoiSpawning, XStart, YStart, +}; + +pub fn forest_builder( + new_depth: i32, + _rng: &mut RandomNumberGenerator, + width: i32, + height: i32, +) -> BuilderChain { + let mut chain = BuilderChain::new(new_depth, width, height, "Into the Woods"); + + chain + .start_with(CellularAutomataBuilder::new()) + .with(AreaStartingPosition::new(XStart::Center, YStart::Center)) + .with(CullUnreachable::new()) + .with(AreaStartingPosition::new(XStart::Left, YStart::Center)); + + // Setup an exit and spawn mobs + chain.with(VoronoiSpawning::new()).with(DistantExit::new()); + + chain +} diff --git a/src/map_builders/town.rs b/src/map_builders/town.rs index dbf9ba8..4049bd5 100644 --- a/src/map_builders/town.rs +++ b/src/map_builders/town.rs @@ -53,8 +53,10 @@ impl TownBuilder { let doors = self.add_doors(rng, build_data, &mut buildings, wall_gap_y); self.add_paths(build_data, &doors); - let exit_idx = build_data.map.xy_idx(build_data.width - 5, wall_gap_y); - build_data.map.tiles[exit_idx] = TileType::DownStairs; + for y in wall_gap_y - 3..wall_gap_y + 4 { + let exit_idx = build_data.map.xy_idx(build_data.width - 2, y); + build_data.map.tiles[exit_idx] = TileType::DownStairs; + } let building_size = self.sort_buildings(&buildings); self.building_factory(rng, build_data, &buildings, &building_size);