diff --git a/Cargo.lock b/Cargo.lock index 988b844..d199de0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,7 @@ dependencies = [ "rand", "specs", "specs-derive", + "thiserror", ] [[package]] @@ -506,6 +507,26 @@ dependencies = [ "unicode-xid", ] +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "tuple_utils" version = "0.3.0" diff --git a/Cargo.toml b/Cargo.toml index 759a63c..3e3444e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,3 +13,4 @@ rand = "0.8.4" grid = "0.6.0" float-ord = "0.3.2" pathfinding = "3" +thiserror = "1" diff --git a/src/io.rs b/src/io.rs new file mode 100644 index 0000000..4562ee1 --- /dev/null +++ b/src/io.rs @@ -0,0 +1,92 @@ +//! Pancurses boilerplate code. + +use std::process::exit; + +use pancurses::{ + endwin, has_colors, init_pair, initscr, noecho, start_color, ColorPair, Window, COLORS, + COLOR_PAIRS, +}; +use thiserror::Error; + +/// Initializes the terminal to accept user input, and creates a new +/// Window. +pub fn init_window() -> Window { + // Create a new window over the terminal. + let window = initscr(); + + // Enable keypad mode (off by default for historical reasons), so + // we can read special keycodes other than just characters. + window.keypad(true); + + // Disable echoing so the user doesn't see flickering in the + // upper-left corner of the screen when they type a character. + noecho(); + + // // Set up a color palette. TODO + // init_colors().unwrap(); + let c = init_colors(); + if let Err(e) = c { + endwin(); + println!("{}", e); + panic!(); + } + + window +} + +/// Cleans everything up and exits the game. +pub fn quit() -> ! { + endwin(); + + exit(0) +} + +/// The colors on a terminal. +#[allow(unused)] +pub enum Color { + Black = 0, + Red = 1, + Green = 2, + Brown = 3, + Blue = 4, + Magenta = 5, + Cyan = 6, + White = 7, +} + +#[derive(Error, Debug)] +enum ColorError { + #[error("colors not supported")] + NoColors, + + #[error("too few colors (have {0}, need 8)")] + NotEnoughColors(u32), + + #[error("too few color slots (have {0}, need 8)")] + NotEnoughSlots(u32), +} + +fn init_colors() -> Result<(), ColorError> { + assert_eq!(start_color(), 0); + if !has_colors() { + Err(ColorError::NoColors) + } else if COLORS() < 8 { + Err(ColorError::NotEnoughColors(COLORS() as _)) + } else if COLOR_PAIRS() < 8 { + Err(ColorError::NotEnoughSlots(COLOR_PAIRS() as _)) + } else { + for n in 0..8 { + init_pair(n, n, Color::Black as _); + } + + Ok(()) + } +} + +pub fn set_color(win: &Window, c: Color) { + // This gets called for every single character; could be a + // performance bottleneck depending on how ncurses implements it. + if has_colors() { + win.attron(ColorPair(c as _)); + } +} diff --git a/src/level.rs b/src/level.rs index e72a3e9..e0ff253 100644 --- a/src/level.rs +++ b/src/level.rs @@ -4,10 +4,7 @@ use pancurses::Window; use rand::Rng; use specs::prelude::*; -use crate::{ - components::{CharRender, Position}, - rooms, -}; +use crate::{components::{CharRender, Position}, io::{Color, set_color}, rooms}; /// The size of a dungeon level, in tiles. pub const LEVEL_SIZE: (usize, usize) = (80, 24); @@ -108,6 +105,12 @@ impl DungeonLevel { for y in 0..LEVEL_SIZE.1 { win.mv(y as _, 0); for x in 0..LEVEL_SIZE.0 { + if (x + y) % 2 == 0 { + set_color(win, Color::Cyan); + } else { + set_color(win, Color::Red); + } + win.addch(if filter((x as _, y as _)) { self.render_tile(x, y) } else { diff --git a/src/main.rs b/src/main.rs index 991808f..acc8efa 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,15 +1,14 @@ -use std::process::exit; - use components::{register_all, CharRender, MobAction, Mobile, Player, Position, TurnTaker}; +use io::init_window; use level::DungeonLevel; -use pancurses::{endwin, initscr, noecho, Window}; use player::player_turn; use rand::thread_rng; use specs::prelude::*; use systems::{MobSystem, TimeSystem}; mod components; +mod io; mod level; mod player; mod rooms; @@ -65,27 +64,3 @@ fn main() { } } } - -/// Initializes the terminal to accept user input, and creates a new -/// Window. -fn init_window() -> Window { - // Create a new window over the terminal. - let window = initscr(); - - // Enable keypad mode (off by default for historical reasons), so - // we can read special keycodes other than just characters. - window.keypad(true); - - // Disable echoing so the user doesn't see flickering in the - // upper-left corner of the screen when they type a character. - noecho(); - - window -} - -/// Cleans everything up and exits the game. -fn quit() -> ! { - endwin(); - - exit(0) -} diff --git a/src/player.rs b/src/player.rs index 053ad1b..7a02814 100644 --- a/src/player.rs +++ b/src/player.rs @@ -6,7 +6,7 @@ use specs::prelude::*; use crate::{ components::{CharRender, MobAction, Mobile, Player, Position}, level::DungeonLevel, - quit, + io::quit, visibility::{visible, CellVisibility, Lighting}, };