Start on new interface rendering

This commit is contained in:
Timothy Warren 2022-01-04 12:12:08 -05:00
parent 83647ae28d
commit bd81fbd9d9
7 changed files with 134 additions and 97 deletions

View File

@ -5,9 +5,10 @@ use crate::{Hidden, Map, Position, Renderable, TileType};
const SHOW_BOUNDARIES: bool = false; const SHOW_BOUNDARIES: bool = false;
pub fn get_screen_bounds(ecs: &World, ctx: &mut Rltk) -> (i32, i32, i32, i32) { pub fn get_screen_bounds(ecs: &World, _ctx: &mut Rltk) -> (i32, i32, i32, i32) {
let player_pos = ecs.fetch::<Point>(); let player_pos = ecs.fetch::<Point>();
let (x_chars, y_chars) = ctx.get_char_size(); // let (x_chars, y_chars) = ctx.get_char_size();
let (x_chars, y_chars) = (48, 44);
let center_x = (x_chars / 2) as i32; let center_x = (x_chars / 2) as i32;
let center_y = (y_chars / 2) as i32; let center_y = (y_chars / 2) as i32;

View File

@ -2,102 +2,129 @@ use ::rltk::{Point, Rltk, VirtualKeyCode, RGB};
use ::specs::prelude::*; use ::specs::prelude::*;
use crate::components::{ use crate::components::{
HungerClock, HungerState, InBackpack, Name, Player, Pools, Position, Viewshed, Attribute, HungerClock, HungerState, InBackpack, Name, Player, Pools, Position, Viewshed,
}; };
use crate::game_log::GameLog; use crate::game_log::GameLog;
use crate::rex_assets::RexAssets; use crate::rex_assets::RexAssets;
use crate::{camera, Equipped, Hidden, Map, RunState, State}; use crate::{camera, Equipped, Hidden, Map, RunState, State};
pub fn draw_hollow_box(
console: &mut Rltk,
sx: i32,
sy: i32,
width: i32,
height: i32,
fg: RGB,
bg: RGB,
) {
use rltk::to_cp437;
console.set(sx, sy, fg, bg, to_cp437('┌'));
console.set(sx + width, sy, fg, bg, to_cp437('┐'));
console.set(sx, sy + height, fg, bg, to_cp437('└'));
console.set(sx + width, sy + height, fg, bg, to_cp437('┘'));
for x in sx + 1..sx + width {
console.set(x, sy, fg, bg, to_cp437('─'));
console.set(x, sy + height, fg, bg, to_cp437('─'));
}
for y in sy + 1..sy + height {
console.set(sx, y, fg, bg, to_cp437('│'));
console.set(sx + width, y, fg, bg, to_cp437('│'));
}
}
pub fn draw_ui(ecs: &World, ctx: &mut Rltk) { pub fn draw_ui(ecs: &World, ctx: &mut Rltk) {
ctx.draw_box( use rltk::to_cp437;
0, let box_gray: RGB = RGB::from_hex("#999999").expect("Ooops");
43, let black = RGB::named(rltk::BLACK);
79, let white = RGB::named(rltk::WHITE);
6,
RGB::named(rltk::WHITE),
RGB::named(rltk::BLACK),
);
let combat_stats = ecs.read_storage::<Pools>(); draw_hollow_box(ctx, 0, 0, 79, 59, box_gray, black); // Overall box
let players = ecs.read_storage::<Player>(); draw_hollow_box(ctx, 0, 0, 49, 45, box_gray, black); // Map box
let hunger = ecs.read_storage::<HungerClock>(); draw_hollow_box(ctx, 0, 45, 79, 14, box_gray, black); // Log box
draw_hollow_box(ctx, 49, 0, 30, 8, box_gray, black); // Top-right panel
// Display player health ctx.set(0, 45, box_gray, black, to_cp437('├'));
for (_player, stats, hc) in (&players, &combat_stats, &hunger).join() { ctx.set(49, 8, box_gray, black, to_cp437('├'));
let health = format!( ctx.set(49, 0, box_gray, black, to_cp437('┬'));
" HP: {} / {} ", ctx.set(49, 45, box_gray, black, to_cp437('┴'));
stats.hit_points.current, stats.hit_points.max ctx.set(79, 8, box_gray, black, to_cp437('┤'));
); ctx.set(79, 45, box_gray, black, to_cp437('┤'));
ctx.print_color(
12,
43,
RGB::named(rltk::YELLOW),
RGB::named(rltk::BLACK),
&health,
);
ctx.draw_bar_horizontal(
29,
43,
51,
stats.hit_points.current,
stats.hit_points.max,
RGB::named(rltk::RED),
RGB::named(rltk::BLACK),
);
match hc.state {
HungerState::WellFed => ctx.print_color(
71,
42,
RGB::named(rltk::GREEN),
RGB::named(rltk::BLACK),
"Well Fed",
),
HungerState::Normal => {}
HungerState::Hungry => ctx.print_color(
71,
42,
RGB::named(rltk::ORANGE),
RGB::named(rltk::BLACK),
"Hungry",
),
HungerState::Starving => ctx.print_color(
71,
42,
RGB::named(rltk::RED),
RGB::named(rltk::BLACK),
"Starving",
),
}
}
// Draw the town name
let map = ecs.fetch::<Map>(); let map = ecs.fetch::<Map>();
let depth = format!("Depth: {}", map.depth); let name_length = map.name.len() + 2;
ctx.print_color( let x_pos = (22 - (name_length / 2)) as i32;
2, ctx.set(x_pos, 0, box_gray, black, to_cp437('┤'));
43, ctx.set(
RGB::named(rltk::YELLOW), x_pos + name_length as i32,
RGB::named(rltk::BLACK), 0,
&depth, box_gray,
black,
to_cp437('├'),
); );
ctx.print_color(x_pos + 1, 0, white, black, &map.name);
std::mem::drop(map);
// Display logs // Draw stats
let log = ecs.fetch::<GameLog>(); let player_entity = ecs.fetch::<Entity>();
let mut y = 44; let pools = ecs.read_storage::<Pools>();
for s in log.entries.iter().rev() { let player_pools = pools.get(*player_entity).unwrap();
if y < 49 { let health = format!(
ctx.print(2, y, s); "Health: {}/{}",
player_pools.hit_points.current, player_pools.hit_points.max
);
let mana = format!(
"Mana: {}/{}",
player_pools.mana.current, player_pools.mana.max
);
ctx.print_color(50, 1, white, black, &health);
ctx.print_color(50, 2, white, black, &mana);
ctx.draw_bar_horizontal(
64,
1,
14,
player_pools.hit_points.current,
player_pools.hit_points.max,
RGB::named(rltk::RED),
RGB::named(rltk::BLACK),
);
ctx.draw_bar_horizontal(
64,
2,
14,
player_pools.mana.current,
player_pools.mana.max,
RGB::named(rltk::BLUE),
RGB::named(rltk::BLACK),
);
}
fn draw_attribute(name: &str, attribute: &Attribute, y: i32, ctx: &mut Rltk) {
let black = RGB::named(rltk::BLACK);
let attr_gray = RGB::from_hex("#CCCCCC").expect("Oops");
ctx.print_color(50, y, attr_gray, black, name);
let color = if attribute.modifiers < 0 {
RGB::from_f32(1.0, 0.0, 0.0)
} else if attribute.modifiers == 0 {
RGB::named(rltk::WHITE)
} else {
RGB::from_f32(0.0, 1.0, 0.0)
};
ctx.print_color(
67,
y,
color,
black,
&format!("{}", attribute.base + attribute.modifiers),
);
ctx.print_color(73, y, color, black, &format!("{}", attribute.bonus));
if attribute.bonus > 0 {
ctx.set(72, y, color, black, rltk::to_cp437('+'));
} }
y += 1;
}
// Mouse cursor
let mouse_pos = ctx.mouse_pos();
ctx.set_bg(mouse_pos.0, mouse_pos.1, RGB::named(rltk::MAGENTA));
draw_tooltips(ecs, ctx);
} }
fn draw_tooltips(ecs: &World, ctx: &mut Rltk) { fn draw_tooltips(ecs: &World, ctx: &mut Rltk) {

View File

@ -53,7 +53,7 @@ macro_rules! register {
} }
} }
const SHOW_MAPGEN_VISUALIZER: bool = true; const SHOW_MAPGEN_VISUALIZER: bool = false;
#[derive(PartialEq, Copy, Clone)] #[derive(PartialEq, Copy, Clone)]
pub enum RunState { pub enum RunState {
@ -486,7 +486,8 @@ impl State {
} }
fn main() -> ::rltk::BError { fn main() -> ::rltk::BError {
let context = ::rltk::RltkBuilder::simple80x50() let context = ::rltk::RltkBuilder::simple(80, 60)
.unwrap()
.with_title("Roguelike Tutorial") .with_title("Roguelike Tutorial")
.build()?; .build()?;
@ -544,7 +545,7 @@ fn main() -> ::rltk::BError {
raws::load_raws(); raws::load_raws();
gs.ecs.insert(Map::new(1, 64, 64)); gs.ecs.insert(Map::new(1, 64, 64, "New Map"));
gs.ecs.insert(Point::zero()); gs.ecs.insert(Point::zero());
gs.ecs.insert(RandomNumberGenerator::new()); gs.ecs.insert(RandomNumberGenerator::new());

View File

@ -20,6 +20,7 @@ pub struct Map {
pub depth: i32, pub depth: i32,
pub bloodstains: HashSet<usize>, pub bloodstains: HashSet<usize>,
pub view_blocked: HashSet<usize>, pub view_blocked: HashSet<usize>,
pub name: String,
#[serde(skip_serializing)] #[serde(skip_serializing)]
#[serde(skip_deserializing)] #[serde(skip_deserializing)]
@ -54,7 +55,7 @@ impl Map {
} }
/// Generates an empty map, consisting entirely of solid walls /// Generates an empty map, consisting entirely of solid walls
pub fn new(new_depth: i32, width: i32, height: i32) -> Map { pub fn new<S: ToString>(new_depth: i32, width: i32, height: i32, name: S) -> Map {
let map_tile_count = (width * height) as usize; let map_tile_count = (width * height) as usize;
Map { Map {
@ -68,6 +69,7 @@ impl Map {
depth: new_depth, depth: new_depth,
bloodstains: HashSet::new(), bloodstains: HashSet::new(),
view_blocked: HashSet::new(), view_blocked: HashSet::new(),
name: name.to_string(),
} }
} }
} }

View File

@ -73,10 +73,10 @@ pub struct BuilderMap {
} }
impl BuilderMap { impl BuilderMap {
fn new(new_depth: i32, width: i32, height: i32) -> BuilderMap { fn new<S: ToString>(new_depth: i32, width: i32, height: i32, name: S) -> BuilderMap {
BuilderMap { BuilderMap {
spawn_list: Vec::new(), spawn_list: Vec::new(),
map: Map::new(new_depth, width, height), map: Map::new(new_depth, width, height, name),
starting_position: None, starting_position: None,
rooms: None, rooms: None,
corridors: None, corridors: None,
@ -104,11 +104,11 @@ pub struct BuilderChain {
} }
impl BuilderChain { impl BuilderChain {
pub fn new(new_depth: i32, width: i32, height: i32) -> BuilderChain { pub fn new<S: ToString>(new_depth: i32, width: i32, height: i32, name: S) -> BuilderChain {
BuilderChain { BuilderChain {
starter: None, starter: None,
builders: Vec::new(), builders: Vec::new(),
build_data: BuilderMap::new(new_depth, width, height), build_data: BuilderMap::new(new_depth, width, height, name),
} }
} }
@ -301,7 +301,7 @@ pub fn random_builder(
width: i32, width: i32,
height: i32, height: i32,
) -> BuilderChain { ) -> BuilderChain {
let mut builder = BuilderChain::new(new_depth, width, height); let mut builder = BuilderChain::new(new_depth, width, height, "New Map");
match rng.roll_dice(1, 2) { match rng.roll_dice(1, 2) {
1 => random_room_builder(rng, &mut builder), 1 => random_room_builder(rng, &mut builder),

View File

@ -11,7 +11,7 @@ pub fn town_builder(
width: i32, width: i32,
height: i32, height: i32,
) -> BuilderChain { ) -> BuilderChain {
let mut chain = BuilderChain::new(new_depth, width, height); let mut chain = BuilderChain::new(new_depth, width, height, "The Town of Ion");
chain.start_with(TownBuilder::new()); chain.start_with(TownBuilder::new());
chain chain

View File

@ -33,7 +33,12 @@ impl WaveformCollapseBuilder {
let constraints = patterns_to_constraints(patterns, CHUNK_SIZE); let constraints = patterns_to_constraints(patterns, CHUNK_SIZE);
self.render_tile_gallery(&constraints, CHUNK_SIZE, build_data); self.render_tile_gallery(&constraints, CHUNK_SIZE, build_data);
build_data.map = Map::new(build_data.map.depth, build_data.width, build_data.height); build_data.map = Map::new(
build_data.map.depth,
build_data.width,
build_data.height,
&build_data.map.name,
);
loop { loop {
let mut solver = Solver::new(constraints.clone(), CHUNK_SIZE, &build_data.map); let mut solver = Solver::new(constraints.clone(), CHUNK_SIZE, &build_data.map);
while !solver.iteration(&mut build_data.map, rng) { while !solver.iteration(&mut build_data.map, rng) {
@ -54,7 +59,7 @@ impl WaveformCollapseBuilder {
chunk_size: i32, chunk_size: i32,
build_data: &mut BuilderMap, build_data: &mut BuilderMap,
) { ) {
build_data.map = Map::new(0, build_data.width, build_data.height); build_data.map = Map::new(0, build_data.width, build_data.height, &build_data.map.name);
let mut counter = 0; let mut counter = 0;
let mut x = 1; let mut x = 1;
let mut y = 1; let mut y = 1;
@ -71,7 +76,8 @@ impl WaveformCollapseBuilder {
if y + chunk_size > build_data.map.height { if y + chunk_size > build_data.map.height {
// Move to the next page // Move to the next page
build_data.take_snapshot(); build_data.take_snapshot();
build_data.map = Map::new(0, build_data.width, build_data.height); build_data.map =
Map::new(0, build_data.width, build_data.height, &build_data.map.name);
x = 1; x = 1;
y = 1; y = 1;