building and theming the forest

This commit is contained in:
Llywelwyn 2023-07-30 04:17:20 +01:00
parent 3e9ebaa002
commit 42113ad6a4
12 changed files with 327 additions and 111 deletions

View file

@ -96,6 +96,24 @@
"vision_range": 4, "vision_range": 4,
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }] "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }]
}, },
{
"id": "deer_little",
"name": "fawn",
"renderable": { "glyph": "q", "fg": "#a57037", "bg": "#000000", "order": 1 },
"flags": ["BYSTANDER", "BLOCKS_TILE"],
"bac": 8,
"vision_range": 8,
"attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }]
},
{
"id": "sheep_little",
"name": "lamb",
"renderable": { "glyph": "q", "fg": "#e7e7e7", "bg": "#000000", "order": 1 },
"flags": ["BYSTANDER", "BLOCKS_TILE"],
"bac": 10,
"vision_range": 4,
"attacks": [{ "name": "kicks", "hit_bonus": 0, "damage": "1d2" }]
},
{ {
"id": "chicken_little", "id": "chicken_little",
"name": "chick", "name": "chick",
@ -105,6 +123,45 @@
"vision_range": 4, "vision_range": 4,
"attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }] "attacks": [{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }]
}, },
{
"id": "horse_little",
"name": "pony",
"renderable": { "glyph": "u", "fg": "#b36c29", "bg": "#000000", "order": 1 },
"flags": ["BYSTANDER", "BLOCKS_TILE"],
"level": 3,
"bac": 6,
"vision_range": 8,
"attacks": [
{ "name": "kicks", "hit_bonus": 0, "damage": "1d6" },
{ "name": "bites", "hit_bonus": 0, "damage": "1d2" }
]
},
{
"id": "horse",
"name": "horse",
"renderable": { "glyph": "u", "fg": "#744d29", "bg": "#000000", "order": 1 },
"flags": ["MONSTER", "BLOCKS_TILE"],
"level": 5,
"bac": 5,
"vision_range": 8,
"attacks": [
{ "name": "kicks", "hit_bonus": 0, "damage": "1d8" },
{ "name": "bites", "hit_bonus": 0, "damage": "1d3" }
]
},
{
"id": "horse_large",
"name": "warhorse",
"renderable": { "glyph": "u", "fg": "#8a3520", "bg": "#000000", "order": 1 },
"flags": ["MONSTER", "BLOCKS_TILE"],
"level": 7,
"bac": 4,
"vision_range": 8,
"attacks": [
{ "name": "kicks", "hit_bonus": 0, "damage": "1d10" },
{ "name": "bites", "hit_bonus": 0, "damage": "1d4" }
]
},
{ {
"id": "rat_giant", "id": "rat_giant",
"name": "giant rat", "name": "giant rat",

View file

@ -67,6 +67,8 @@
{ "id": "kobold", "weight": 1, "difficulty": 1}, { "id": "kobold", "weight": 1, "difficulty": 1},
{ "id": "fox", "weight": 1, "difficulty": 1}, { "id": "fox", "weight": 1, "difficulty": 1},
{ "id": "jackal", "weight": 4, "difficulty": 1}, { "id": "jackal", "weight": 4, "difficulty": 1},
{ "id": "deer_little", "weight": 1, "difficulty": 1},
{ "id": "sheep_little", "weight": 1, "difficulty": 1},
{ "id": "rat_giant", "weight": 2, "difficulty": 2}, { "id": "rat_giant", "weight": 2, "difficulty": 2},
{ "id": "coyote", "weight": 4, "difficulty": 2}, { "id": "coyote", "weight": 4, "difficulty": 2},
{ "id": "dog_little", "weight": 1, "difficulty": 3}, { "id": "dog_little", "weight": 1, "difficulty": 3},
@ -74,9 +76,12 @@
{ "id": "orc_large", "weight": 1, "difficulty": 3}, { "id": "orc_large", "weight": 1, "difficulty": 3},
{ "id": "goblin_chieftain", "weight": 1, "difficulty": 3}, { "id": "goblin_chieftain", "weight": 1, "difficulty": 3},
{ "id": "ogre", "weight": 1, "difficulty": 4}, { "id": "ogre", "weight": 1, "difficulty": 4},
{ "id": "horse_little", "weight": 2, "difficulty": 4},
{ "id": "dog", "weight": 1, "difficulty": 5}, { "id": "dog", "weight": 1, "difficulty": 5},
{ "id": "wolf", "weight": 2, "difficulty": 6}, { "id": "wolf", "weight": 2, "difficulty": 6},
{ "id": "dog_large", "weight": 1, "difficulty": 7} { "id": "dog_large", "weight": 1, "difficulty": 7},
{ "id": "horse", "weight": 2, "difficulty": 7},
{ "id": "horse_large", "weight": 2, "difficulty": 9}
] ]
}, },
{ {

View file

@ -33,7 +33,7 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height { if t_x >= 0 && t_x < map.width && t_y >= 0 && t_y < map.height {
let idx = map.xy_idx(t_x, t_y); let idx = map.xy_idx(t_x, t_y);
if map.revealed_tiles[idx] { if map.revealed_tiles[idx] {
let (glyph, fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map); let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(idx, &*map);
ctx.set(x + x_offset, y + y_offset, fg, bg, glyph); ctx.set(x + x_offset, y + y_offset, fg, bg, glyph);
} }
} else if SHOW_BOUNDARIES { } else if SHOW_BOUNDARIES {
@ -63,7 +63,7 @@ pub fn render_camera(ecs: &World, ctx: &mut Rltk) {
if pos.x < max_x && pos.y < max_y && pos.x >= min_x && pos.y >= min_y { if pos.x < max_x && pos.y < max_y && pos.x >= min_x && pos.y >= min_y {
let mut draw = false; let mut draw = false;
let mut fg = render.fg; let mut fg = render.fg;
let (_glyph, _fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map); let (_glyph, _fg, bg) = crate::map::themes::get_tile_renderables_for_id(idx, &*map);
// Draw entities on visible tiles // Draw entities on visible tiles
if map.visible_tiles[idx] { if map.visible_tiles[idx] {
draw = true; draw = true;
@ -114,7 +114,7 @@ pub fn render_debug_map(map: &Map, ctx: &mut Rltk) {
if tx >= 0 && tx < map_width && ty >= 0 && ty < map_height { if tx >= 0 && tx < map_width && ty >= 0 && ty < map_height {
let idx = map.xy_idx(tx, ty); let idx = map.xy_idx(tx, ty);
if map.revealed_tiles[idx] { if map.revealed_tiles[idx] {
let (glyph, fg, bg) = crate::map::themes::get_tile_glyph(idx, &*map); let (glyph, fg, bg) = crate::map::themes::get_tile_renderables_for_id(idx, &*map);
ctx.set(x, y, fg, bg, glyph); ctx.set(x, y, fg, bg, glyph);
} }
} else if SHOW_BOUNDARIES { } else if SHOW_BOUNDARIES {

View file

@ -26,17 +26,21 @@ impl Tooltip {
return self.lines.len() as i32 + 2i32; return self.lines.len() as i32 + 2i32;
} }
fn render(&self, ctx: &mut Rltk, x: i32, y: i32) { fn render(&self, ctx: &mut Rltk, x: i32, y: i32) {
let white = RGB::named(rltk::WHITE);
let weak = RGB::named(rltk::CYAN); let weak = RGB::named(rltk::CYAN);
let strong = RGB::named(rltk::ORANGE); let strong = RGB::named(rltk::ORANGE);
let attribute = RGB::named(rltk::PINK);
ctx.draw_box(x, y, self.width() - 1, self.height() - 1, RGB::named(WHITE), RGB::named(BLACK)); ctx.draw_box(x, y, self.width() - 1, self.height() - 1, RGB::named(WHITE), RGB::named(BLACK));
for (i, s) in self.lines.iter().enumerate() { for (i, s) in self.lines.iter().enumerate() {
let col = if i == 0 { let col = if i == 0 {
RGB::named(WHITE) white
} else if s.starts_with('-') { } else if s.starts_with('-') {
weak weak
} else { } else if s.starts_with('*') {
strong strong
} else {
attribute
}; };
ctx.print_color(x + 1, y + i as i32 + 1, col, RGB::named(BLACK), &s); ctx.print_color(x + 1, y + i as i32 + 1, col, RGB::named(BLACK), &s);
} }
@ -91,9 +95,12 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
if a.intelligence.bonus > 2 { s += "smart "}; if a.intelligence.bonus > 2 { s += "smart "};
if a.wisdom.bonus < -2 { s += "unwise "}; if a.wisdom.bonus < -2 { s += "unwise "};
if a.wisdom.bonus > 2 { s += "wisened "}; if a.wisdom.bonus > 2 { s += "wisened "};
if a.charisma.bonus < -2 { s += "ugly "}; if a.charisma.bonus < -2 { s += "ugly"};
if a.charisma.bonus > 2 { s += "attractive "}; if a.charisma.bonus > 2 { s += "attractive"};
if !s.is_empty() { if !s.is_empty() {
if s.ends_with(" ") {
s.pop();
}
tip.add(s); tip.add(s);
} }
} }
@ -114,13 +121,12 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
if tooltips.is_empty() { return ; } if tooltips.is_empty() { return ; }
let box_gray : RGB = RGB::from_hex("#999999").expect("Oops");
let white = RGB::named(rltk::WHITE); let white = RGB::named(rltk::WHITE);
let arrow; let arrow;
let arrow_x; let arrow_x;
let arrow_y = mouse_pos.1; let arrow_y = mouse_pos.1;
if mouse_pos.0 < 50 { if mouse_pos.0 < 35 {
// Render to the left // Render to the left
arrow = to_cp437('→'); arrow = to_cp437('→');
arrow_x = mouse_pos.0 - 1; arrow_x = mouse_pos.0 - 1;
@ -129,7 +135,7 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
arrow = to_cp437('←'); arrow = to_cp437('←');
arrow_x = mouse_pos.0 + 1; arrow_x = mouse_pos.0 + 1;
} }
ctx.set(arrow_x, arrow_y, white, box_gray, arrow); ctx.set(arrow_x, arrow_y, white, RGB::named(rltk::BLACK), arrow);
let mut total_height = 0; let mut total_height = 0;
for t in tooltips.iter() { for t in tooltips.iter() {
@ -142,10 +148,10 @@ pub fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {
} }
for t in tooltips.iter() { for t in tooltips.iter() {
let x = if mouse_pos.0 < 40 { let x = if mouse_pos.0 < 35 {
mouse_pos.0 - (1 + t.width()) mouse_pos.0 - (1 + t.width())
} else { } else {
mouse_pos.0 + (1 + t.width()) mouse_pos.0 + (1 + 1)
}; };
t.render(ctx, x, y); t.render(ctx, x, y);
y += t.height(); y += t.height();

View file

@ -43,7 +43,7 @@ mod rex_assets;
extern crate lazy_static; extern crate lazy_static;
//Consts //Consts
pub const SHOW_MAPGEN: bool = false; pub const SHOW_MAPGEN: bool = true;
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum RunState { pub enum RunState {

24
src/map/colours.rs Normal file
View file

@ -0,0 +1,24 @@
pub const NON_VISIBLE_MULTIPLIER: f32 = 0.7;
pub const BLOODSTAIN_COLOUR: (u8, u8, u8) = (153, 0, 0);
// DEFAULT THEME
pub const DEFAULT_BG_COLOUR: (u8, u8, u8) = (29, 50, 50);
pub const WALL_COLOUR: (u8, u8, u8) = (229, 191, 94);
pub const FLOOR_COLOUR: (u8, u8, u8) = (25, 204, 122);
pub const DOWN_STAIR_COLOUR: (u8, u8, u8) = (200, 200, 0);
pub const WOOD_FLOOR_COLOUR: (u8, u8, u8) = (41, 30, 20);
pub const FENCE_FG_COLOUR: (u8, u8, u8) = (110, 24, 0);
pub const FENCE_COLOUR: (u8, u8, u8) = (45, 30, 10);
pub const BRIDGE_COLOUR: (u8, u8, u8) = (42, 48, 37);
pub const GRAVEL_COLOUR: (u8, u8, u8) = (26, 26, 53);
pub const ROAD_COLOUR: (u8, u8, u8) = (8, 38, 40);
pub const GRASS_COLOUR: (u8, u8, u8) = (9, 65, 6);
pub const FOLIAGE_COLOUR: (u8, u8, u8) = (5, 60, 5);
pub const HEAVY_FOLIAGE_COLOUR: (u8, u8, u8) = (5, 55, 5);
pub const SAND_COLOUR: (u8, u8, u8) = (70, 70, 21);
pub const SHALLOW_WATER_COLOUR: (u8, u8, u8) = (24, 47, 99);
pub const DEEP_WATER_COLOUR: (u8, u8, u8) = (18, 33, 63);
pub const BARS_COLOUR: (u8, u8, u8) = (100, 100, 100);
// FOREST THEME
pub const FOREST_WALL_COLOUR: (u8, u8, u8) = (0, 153, 0);

19
src/map/glyphs.rs Normal file
View file

@ -0,0 +1,19 @@
// DEFAULT THEME
pub const WALL_GLYPH: char = '#';
pub const FLOOR_GLYPH: char = '.';
pub const DOWN_STAIR_GLYPH: char = '>';
pub const WOOD_FLOOR_GLYPH: char = '.';
pub const FENCE_GLYPH: char = '=';
pub const BRIDGE_GLYPH: char = '.';
pub const GRAVEL_GLYPH: char = ';';
pub const ROAD_GLYPH: char = '.';
pub const GRASS_GLYPH: char = '"';
pub const FOLIAGE_GLYPH: char = ':';
pub const HEAVY_FOLIAGE_GLYPH: char = ';';
pub const SAND_GLYPH: char = '.';
pub const SHALLOW_WATER_GLYPH: char = '~';
pub const DEEP_WATER_GLYPH: char = '≈';
pub const BARS_GLYPH: char = '#';
// FOREST THEME
pub const FOREST_WALL_GLYPH: char = '♣';

View file

@ -2,6 +2,8 @@ use rltk::{Algorithm2D, BaseMap, Point};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use specs::prelude::*; use specs::prelude::*;
use std::collections::HashSet; use std::collections::HashSet;
pub mod colours;
mod glyphs;
mod tiletype; mod tiletype;
pub use tiletype::{tile_cost, tile_opaque, tile_walkable, TileType}; pub use tiletype::{tile_cost, tile_opaque, tile_walkable, TileType};
pub mod themes; pub mod themes;
@ -50,7 +52,7 @@ impl Map {
lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false. lit_tiles: vec![true; map_tile_count], // NYI: Light sources. Once those exist, we can set this to false.
telepath_tiles: vec![false; map_tile_count], telepath_tiles: vec![false; map_tile_count],
colour_offset: vec![(1.0, 1.0, 1.0); map_tile_count], colour_offset: vec![(1.0, 1.0, 1.0); map_tile_count],
additional_fg_offset: rltk::RGB::from_u8(HALF_OFFSET, HALF_OFFSET, HALF_OFFSET), additional_fg_offset: rltk::RGB::from_u8(OFFSET_PERCENT as u8, OFFSET_PERCENT as u8, OFFSET_PERCENT as u8),
blocked: vec![false; map_tile_count], blocked: vec![false; map_tile_count],
id: new_id, id: new_id,
name: name.to_string(), name: name.to_string(),
@ -60,7 +62,6 @@ impl Map {
tile_content: vec![Vec::new(); map_tile_count], tile_content: vec![Vec::new(); map_tile_count],
}; };
const HALF_OFFSET: u8 = 5;
const OFFSET_PERCENT: i32 = 10; const OFFSET_PERCENT: i32 = 10;
const TWICE_OFFSET: i32 = OFFSET_PERCENT * 2; const TWICE_OFFSET: i32 = OFFSET_PERCENT * 2;
let mut rng = rltk::RandomNumberGenerator::new(); let mut rng = rltk::RandomNumberGenerator::new();

View file

@ -1,109 +1,70 @@
use super::{Map, TileType}; use super::{colours::*, glyphs::*, Map, TileType};
use rltk::RGB; use rltk::RGB;
use std::ops::{Add, Mul}; use std::ops::{Add, Mul};
pub fn offset(rgb: rltk::RGB, offsets: (f32, f32, f32)) -> RGB { pub fn get_tile_renderables_for_id(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
let r = rgb.r * offsets.0; let (glyph, mut fg, mut bg) = match map.id {
let g = rgb.g * offsets.1; 2 => get_forest_theme_renderables(idx, map),
let b = rgb.b * offsets.2; _ => get_default_theme_renderables(idx, map),
};
return rltk::RGB::from_f32(r, g, b); // If one of the colours was left blank, make them the same.
}
pub fn get_tile_glyph(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
let offsets = map.colour_offset[idx];
let glyph: rltk::FontCharType;
let mut fg: RGB = RGB::new();
let mut bg: RGB;
let default_bg: RGB = RGB::from_u8(29, 50, 50);
match map.tiles[idx] {
TileType::Floor => {
glyph = rltk::to_cp437('.');
fg = RGB::from_f32(0.1, 0.8, 0.5);
bg = default_bg;
}
TileType::WoodFloor => {
glyph = rltk::to_cp437('.');
bg = RGB::from_u8(41, 30, 20);
}
TileType::Fence => {
glyph = rltk::to_cp437('=');
fg = RGB::from_u8(110, 24, 0);
bg = RGB::from_u8(45, 30, 10);
}
TileType::Wall => {
let x = idx as i32 % map.width;
let y = idx as i32 / map.width;
glyph = wall_glyph(&*map, x, y);
fg = RGB::from_f32(0.9, 0.75, 0.37);
bg = default_bg;
}
TileType::DownStair => {
glyph = rltk::to_cp437('>');
fg = RGB::from_u8(200, 200, 0);
bg = default_bg;
}
TileType::Bridge => {
glyph = rltk::to_cp437('.');
bg = RGB::from_u8(42, 48, 37);
}
TileType::Gravel => {
glyph = rltk::to_cp437(';');
bg = RGB::from_u8(26, 26, 53);
}
TileType::Road => {
glyph = rltk::to_cp437('.');
//fg = RGB::from_u8(112, 105, 94);
bg = RGB::from_u8(8, 38, 40);
}
TileType::Grass => {
glyph = rltk::to_cp437('"');
bg = RGB::from_u8(9, 65, 6);
}
TileType::Foliage => {
glyph = rltk::to_cp437(':');
bg = RGB::from_u8(5, 60, 5);
}
TileType::HeavyFoliage => {
glyph = rltk::to_cp437(';');
bg = RGB::from_u8(5, 55, 5);
}
TileType::Sand => {
glyph = rltk::to_cp437('.');
bg = RGB::from_u8(70, 70, 21);
}
TileType::ShallowWater => {
glyph = rltk::to_cp437('~');
bg = RGB::from_u8(24, 47, 99);
}
TileType::DeepWater => {
glyph = rltk::to_cp437('≈');
bg = RGB::from_u8(18, 33, 63);
}
}
if map.bloodstains.contains(&idx) {
bg = bg.add(RGB::from_f32(0.6, 0., 0.));
}
// If the foreground hasn't been changed, just add
// the bg to it. Otherwise, leave it as is.
if fg == RGB::new() { if fg == RGB::new() {
fg = fg.add(bg).add(map.additional_fg_offset).add(map.additional_fg_offset); fg = bg
} else if bg == RGB::new() {
bg = fg;
} }
fg = offset(fg, offsets); fg = fg.add(map.additional_fg_offset);
bg = offset(bg, offsets); (fg, bg) = apply_colour_offset(fg, bg, map, idx);
bg = apply_bloodstain_if_necessary(bg, map, idx);
if !map.visible_tiles[idx] { (fg, bg) = darken_if_not_visible(fg, bg, map, idx);
fg = fg.mul(0.7);
bg = bg.mul(0.7);
}
return (glyph, fg, bg); return (glyph, fg, bg);
} }
#[rustfmt::skip]
pub fn get_default_theme_renderables(idx: usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
let glyph: rltk::FontCharType;
let mut fg: RGB = RGB::new();
let mut bg: RGB = RGB::new();
match map.tiles[idx] {
TileType::Floor => { glyph = rltk::to_cp437(FLOOR_GLYPH); fg = RGB::named(FLOOR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); }
TileType::WoodFloor => { glyph = rltk::to_cp437(WOOD_FLOOR_GLYPH); bg = RGB::named(WOOD_FLOOR_COLOUR); }
TileType::Fence => { glyph = rltk::to_cp437(FENCE_GLYPH); fg = RGB::named(FENCE_FG_COLOUR); bg = RGB::named(FENCE_COLOUR); }
TileType::Wall => { let x = idx as i32 % map.width; let y = idx as i32 / map.width; glyph = wall_glyph(&*map, x, y); fg = RGB::named(WALL_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); }
TileType::DownStair => { glyph = rltk::to_cp437(DOWN_STAIR_GLYPH); fg = RGB::named(DOWN_STAIR_COLOUR); bg = RGB::named(DEFAULT_BG_COLOUR); }
TileType::Bridge => { glyph = rltk::to_cp437(BRIDGE_GLYPH); bg = RGB::named(BRIDGE_COLOUR); }
TileType::Gravel => { glyph = rltk::to_cp437(GRAVEL_GLYPH); bg = RGB::named(GRAVEL_COLOUR); }
TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); }
TileType::Grass => { glyph = rltk::to_cp437(GRASS_GLYPH); bg = RGB::named(GRASS_COLOUR); }
TileType::Foliage => { glyph = rltk::to_cp437(FOLIAGE_GLYPH); bg = RGB::named(FOLIAGE_COLOUR); }
TileType::HeavyFoliage => { glyph = rltk::to_cp437(HEAVY_FOLIAGE_GLYPH); bg = RGB::named(HEAVY_FOLIAGE_COLOUR); }
TileType::Sand => { glyph = rltk::to_cp437(SAND_GLYPH); bg = RGB::named(SAND_COLOUR); }
TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); }
TileType::DeepWater => { glyph = rltk::to_cp437(DEEP_WATER_GLYPH); bg = RGB::named(DEEP_WATER_COLOUR); }
TileType::Bars => { glyph = rltk::to_cp437(BARS_GLYPH); fg = RGB::named(BARS_COLOUR); bg = RGB::named(FLOOR_COLOUR); }
}
return (glyph, fg, bg);
}
#[rustfmt::skip]
fn get_forest_theme_renderables(idx:usize, map: &Map) -> (rltk::FontCharType, RGB, RGB) {
let glyph;
let mut fg = RGB::new();
let mut bg = RGB::new();
match map.tiles[idx] {
TileType::Wall => { glyph = rltk::to_cp437(FOREST_WALL_GLYPH); fg = RGB::named(FOREST_WALL_COLOUR); bg = RGB::named(GRASS_COLOUR) }
TileType::Road => { glyph = rltk::to_cp437(ROAD_GLYPH); bg = RGB::named(ROAD_COLOUR); }
TileType::ShallowWater => { glyph = rltk::to_cp437(SHALLOW_WATER_GLYPH); bg = RGB::named(SHALLOW_WATER_COLOUR); }
_ => { (glyph, fg, _) = get_default_theme_renderables(idx, map); bg = RGB::named(GRASS_COLOUR) }
}
(glyph, fg, bg)
}
fn is_revealed_and_wall(map: &Map, x: i32, y: i32) -> bool { fn is_revealed_and_wall(map: &Map, x: i32, y: i32) -> bool {
let idx = map.xy_idx(x, y); let idx = map.xy_idx(x, y);
map.tiles[idx] == TileType::Wall && map.revealed_tiles[idx] map.tiles[idx] == TileType::Wall && map.revealed_tiles[idx]
@ -245,3 +206,33 @@ fn wall_glyph(map: &Map, x: i32, y: i32) -> rltk::FontCharType {
_ => 35, // We missed one? _ => 35, // We missed one?
} }
} }
fn apply_colour_offset(mut fg: RGB, mut bg: RGB, map: &Map, idx: usize) -> (RGB, RGB) {
let offsets = map.colour_offset[idx];
fg = multiply_by_float(fg.add(map.additional_fg_offset), offsets);
bg = multiply_by_float(bg, offsets);
return (fg, bg);
}
fn darken_if_not_visible(mut fg: RGB, mut bg: RGB, map: &Map, idx: usize) -> (RGB, RGB) {
if !map.visible_tiles[idx] {
fg = fg.mul(NON_VISIBLE_MULTIPLIER);
bg = bg.mul(NON_VISIBLE_MULTIPLIER);
}
return (fg, bg);
}
fn apply_bloodstain_if_necessary(mut bg: RGB, map: &Map, idx: usize) -> RGB {
if map.bloodstains.contains(&idx) {
bg = bg.add(RGB::named(BLOODSTAIN_COLOUR));
}
return bg;
}
pub fn multiply_by_float(rgb: rltk::RGB, offsets: (f32, f32, f32)) -> RGB {
let r = rgb.r * offsets.0;
let g = rgb.g * offsets.1;
let b = rgb.b * offsets.2;
return rltk::RGB::from_f32(r, g, b);
}

View file

@ -7,6 +7,7 @@ pub enum TileType {
// Impassable (transparent) // Impassable (transparent)
DeepWater, DeepWater,
Fence, Fence,
Bars,
// Floors (walkable) // Floors (walkable)
Floor, Floor,
WoodFloor, WoodFloor,

109
src/map_builders/forest.rs Normal file
View file

@ -0,0 +1,109 @@
use super::{
AreaStartingPosition, BuilderChain, BuilderMap, CellularAutomataBuilder, CullUnreachable, MetaMapBuilder, TileType,
VoronoiSpawning, XStart, YStart,
};
use rltk::prelude::*;
pub fn forest_builder(
new_id: i32,
_rng: &mut rltk::RandomNumberGenerator,
width: i32,
height: i32,
difficulty: i32,
) -> BuilderChain {
let mut chain = BuilderChain::new(new_id, width, height, difficulty, "Into the Woods");
chain.start_with(CellularAutomataBuilder::new());
chain.with(AreaStartingPosition::new(XStart::CENTRE, YStart::CENTRE));
chain.with(CullUnreachable::new());
chain.with(AreaStartingPosition::new(XStart::LEFT, YStart::CENTRE));
// Setup an exit and spawn mobs
chain.with(VoronoiSpawning::new());
chain.with(RoadExit::new());
return chain;
}
pub struct RoadExit {}
impl MetaMapBuilder for RoadExit {
fn build_map(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
self.build(rng, build_data);
}
}
impl RoadExit {
#[allow(dead_code)]
pub fn new() -> Box<RoadExit> {
return Box::new(RoadExit {});
}
fn find_exit(&self, build_data: &mut BuilderMap, seed_x: i32, seed_y: i32) -> (i32, i32) {
let mut available_floors: Vec<(usize, f32)> = Vec::new();
for (idx, tiletype) in build_data.map.tiles.iter().enumerate() {
if crate::map::tile_walkable(*tiletype) {
available_floors.push((
idx,
DistanceAlg::PythagorasSquared.distance2d(
Point::new(idx as i32 % build_data.map.width, idx as i32 / build_data.map.width),
Point::new(seed_x, seed_y),
),
));
}
}
if available_floors.is_empty() {
panic!("No valid floors to start on.");
}
available_floors.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap());
let end_x = available_floors[0].0 as i32 % build_data.map.width;
let end_y = available_floors[0].0 as i32 / build_data.map.width;
return (end_x, end_y);
}
fn paint_road(&self, build_data: &mut BuilderMap, x: i32, y: i32) {
if x < 1 || x > build_data.map.width - 2 || y < 1 || y > build_data.map.width - 2 {
return;
}
let idx = build_data.map.xy_idx(x, y);
if build_data.map.tiles[idx] != TileType::DownStair {
build_data.map.tiles[idx] = TileType::Road;
}
}
fn build(&mut self, rng: &mut RandomNumberGenerator, build_data: &mut BuilderMap) {
let starting_pos = build_data.starting_position.as_ref().unwrap().clone();
let start_idx = build_data.map.xy_idx(starting_pos.x, starting_pos.y);
let (end_x, end_y) = self.find_exit(build_data, build_data.map.width - 2, build_data.height / 2);
let end_idx = build_data.map.xy_idx(end_x, end_y);
build_data.map.populate_blocked();
let path = a_star_search(start_idx, end_idx, &mut build_data.map);
for idx in path.steps.iter() {
let x = *idx as i32 % build_data.map.width;
let y = *idx as i32 / build_data.map.width;
self.paint_road(build_data, x, y);
self.paint_road(build_data, x - 1, y);
self.paint_road(build_data, x + 1, y);
self.paint_road(build_data, x, y - 1);
self.paint_road(build_data, x, y + 1);
}
build_data.take_snapshot();
let exit_dir = rng.roll_dice(1, 2);
let (seed_x, seed_y, stream_start_x, stream_start_y) = if exit_dir == 1 {
(build_data.map.width - 1, 1, 0, build_data.height - 1)
} else {
(build_data.map.width - 1, build_data.height - 1, 1, build_data.height - 1)
};
let (stairs_x, stairs_y) = self.find_exit(build_data, seed_x, seed_y);
let stairs_idx = build_data.map.xy_idx(stairs_x, stairs_y);
let (stream_x, stream_y) = self.find_exit(build_data, stream_start_x, stream_start_y);
let stream_idx = build_data.map.xy_idx(stream_x, stream_y) as usize;
let stream = a_star_search(stairs_idx, stream_idx, &mut build_data.map);
for tile in stream.steps.iter() {
if build_data.map.tiles[*tile as usize] == TileType::Floor {
build_data.map.tiles[*tile as usize] = TileType::ShallowWater;
}
}
build_data.map.tiles[stairs_idx] = TileType::DownStair;
build_data.take_snapshot();
}
}

View file

@ -60,6 +60,8 @@ mod fill_edges;
use fill_edges::FillEdges; use fill_edges::FillEdges;
mod town; mod town;
use town::town_builder; use town::town_builder;
mod forest;
use forest::forest_builder;
// Shared data to be passed around build chain // Shared data to be passed around build chain
pub struct BuilderMap { pub struct BuilderMap {
@ -342,6 +344,7 @@ pub fn level_builder(new_id: i32, rng: &mut rltk::RandomNumberGenerator, width:
let difficulty = new_id; let difficulty = new_id;
match new_id { match new_id {
1 => town_builder(new_id, rng, width, height), 1 => town_builder(new_id, rng, width, height),
2 => forest_builder(new_id, rng, width, height, difficulty),
_ => random_builder(new_id, rng, width, height, difficulty), _ => random_builder(new_id, rng, width, height, difficulty),
} }
} }