diff --git a/src/main.rs b/src/main.rs index de20c63..78a23ef 100644 --- a/src/main.rs +++ b/src/main.rs @@ -3,14 +3,15 @@ #![allow(unused_mut)] #![allow(dead_code)] +use std::cmp; use tcod::colors::*; use tcod::console::*; -const MAP_WIDTH: i32 = 80; -const MAP_HEIGHT: i32 = 45; const SCREEN_WIDTH: i32 = 80; const SCREEN_HEIGHT: i32 = 50; const FPS: i32 = 20; +const MAP_WIDTH: i32 = 80; +const MAP_HEIGHT: i32 = 45; const COLOR_DARK_WALL: Color = Color { r: 0, g: 0, b: 100 }; const COLOR_DARK_GROUND: Color = Color { r: 50, @@ -23,6 +24,7 @@ struct Tcod { con: Offscreen, } +#[derive(Debug)] struct Object { x: i32, y: i32, @@ -35,10 +37,12 @@ impl Object { Object { x, y, char, color } } - // Move by given amount - pub fn move_by(&mut self, dx: i32, dy: i32) { - self.x += dx; - self.y += dy; + // Move by given amount, if the destination is not blocked + pub fn move_by(&mut self, dx: i32, dy: i32, game: &Game) { + if !game.map[(self.x + dx) as usize][(self.y + dy) as usize].blocked { + self.x += dx; + self.y += dy; + } } // Set the color and then draw the character that represents this object at its position @@ -70,11 +74,36 @@ impl Tile { } } -fn handle_keys(tcod: &mut Tcod, player: &mut Object) -> bool { +type Map = Vec>; + +struct Game { + map: Map, +} + +// A rectangle on the map, used to characterise a room. +#[derive(Clone, Copy, Debug)] +struct Rect { + x1: i32, + y1: i32, + x2: i32, + y2: i32, +} + +impl Rect { + pub fn new(x: i32, y: i32, w: i32, h: i32) -> Self { + Rect { + x1: x, + y1: y, + x2: x + w, + y2: y + h, + } + } +} + +fn handle_keys(tcod: &mut Tcod, game: &Game, player: &mut Object) -> bool { use tcod::input::Key; use tcod::input::KeyCode::*; - // todo: handle keys let key = tcod.root.wait_for_keypress(true); match key { Key { @@ -88,16 +117,86 @@ fn handle_keys(tcod: &mut Tcod, player: &mut Object) -> bool { } Key { code: Escape, .. } => return true, // exit game - Key { code: Up, .. } => player.move_by(0, -1), - Key { code: Down, .. } => player.move_by(0, 1), - Key { code: Left, .. } => player.move_by(-1, 0), - Key { code: Right, .. } => player.move_by(1, 0), + Key { code: Up, .. } => player.move_by(0, -1, game), + Key { code: Down, .. } => player.move_by(0, 1, game), + Key { code: Left, .. } => player.move_by(-1, 0, game), + Key { code: Right, .. } => player.move_by(1, 0, game), _ => {} } false } +fn make_map() -> Map { + // Fill the map with unblocked tiles + let mut map = vec![vec![Tile::wall(); MAP_HEIGHT as usize]; MAP_WIDTH as usize]; + + // Create two rooms + let room1 = Rect::new(20, 15, 10, 15); + let room2 = Rect::new(50, 15, 10, 15); + create_room(room1, &mut map); + create_room(room2, &mut map); + create_h_tunnel(25, 55, 23, &mut map); + + map +} + +fn create_room(room: Rect, map: &mut Map) { + // Go through the tiles in the rectangle and make them passable + for x in (room.x1 + 1)..room.x2 { + for y in (room.y1 + 1)..room.y2 { + map[x as usize][y as usize] = Tile::empty(); + } + } +} + +fn create_h_tunnel(x1: i32, x2: i32, y: i32, map: &mut Map) { + // Horizontal tunnel. `min()` and `max()` are used in case + // `x1 > x2` + for x in cmp::min(x1, x2)..(cmp::max(x1, x2) + 1) { + map[x as usize][y as usize] = Tile::empty(); + } +} + +fn create_v_tunnel(y1: i32, y2: i32, x: i32, map: &mut Map) { + // Vertical tunnel + for y in cmp::min(y1, y2)..(cmp::max(y1, y2) + 1) { + map[x as usize][y as usize] = Tile::empty(); + } +} + +fn render_all(tcod: &mut Tcod, game: &Game, objects: &[Object]) { + // Draw all objects in the list + for object in objects { + object.draw(&mut tcod.con); + } + + // Go through all tiles and set their background color + for y in 0..MAP_HEIGHT { + for x in 0..MAP_WIDTH { + let wall = game.map[x as usize][y as usize].block_sight; + if wall { + tcod.con + .set_char_background(x, y, COLOR_DARK_WALL, BackgroundFlag::Set); + } else { + tcod.con + .set_char_background(x, y, COLOR_DARK_GROUND, BackgroundFlag::Set) + } + } + } + + // Blit contents of "con" to the root console + blit( + &tcod.con, + (0, 0), + (MAP_WIDTH, MAP_HEIGHT), + &mut tcod.root, + (0, 0), + 1.0, + 1.0, + ); +} + fn main() { tcod::system::set_fps(FPS); @@ -108,12 +207,12 @@ fn main() { .title("Rust/libtcod tutorial") .init(); - let con = Offscreen::new(SCREEN_WIDTH, SCREEN_HEIGHT); + let con = Offscreen::new(MAP_WIDTH, MAP_HEIGHT); let mut tcod = Tcod { root, con }; // Create player object - let player = Object::new(SCREEN_WIDTH / 2, SCREEN_HEIGHT / 2, '@', WHITE); + let player = Object::new(25, 23, '@', WHITE); // // Create an NPC let npc = Object::new(SCREEN_WIDTH / 2 - 5, SCREEN_HEIGHT / 2, '@', YELLOW); @@ -121,6 +220,11 @@ fn main() { // List of objects in the world let mut objects = [player, npc]; + let game = Game { + // Generate map + map: make_map(), + }; + while !tcod.root.window_closed() { tcod.con.clear(); @@ -128,19 +232,11 @@ fn main() { object.draw(&mut tcod.con) } - blit( - &tcod.con, - (0, 0), - (SCREEN_WIDTH, SCREEN_HEIGHT), - &mut tcod.root, - (0, 0), - 1.0, - 1.0, - ); + render_all(&mut tcod, &game, &objects); tcod.root.flush(); let player = &mut objects[0]; - let exit = handle_keys(&mut tcod, player); + let exit = handle_keys(&mut tcod, &game, player); if exit { break; }