diff options
author | Iago Garrido <iago086@gmail.com> | 2019-11-13 19:21:00 +0100 |
---|---|---|
committer | Iago Garrido <iago086@gmail.com> | 2019-11-13 19:21:00 +0100 |
commit | b3d2d5811f3d1c153583695c1f338092ca0db20f (patch) | |
tree | e1fec16189079e267c9d752a274dee35838cdcc7 | |
parent | c20f1310fb3417cb1b0bf41e2d31db373751e392 (diff) | |
parent | 3fb8f5651144ef21338c09c14cd3db03c6416136 (diff) |
Merge remote-tracking branch 'upstream/master'
-rw-r--r-- | img/screenshot.png | bin | 3212 -> 225307 bytes | |||
-rw-r--r-- | src/entities.rs | 117 | ||||
-rw-r--r-- | src/main.rs | 45 | ||||
-rw-r--r-- | src/state.rs | 62 | ||||
-rw-r--r-- | src/tiling.rs | 13 | ||||
-rw-r--r-- | src/world.rs | 124 |
6 files changed, 214 insertions, 147 deletions
diff --git a/img/screenshot.png b/img/screenshot.png Binary files differindex 2fe5cbd..ff8ae77 100644 --- a/img/screenshot.png +++ b/img/screenshot.png diff --git a/src/entities.rs b/src/entities.rs index a01d86a..80e1235 100644 --- a/src/entities.rs +++ b/src/entities.rs @@ -1,12 +1,27 @@ use std::cmp;
-use pancurses::{Window};
-use crate::world::{Point};
use crate::tiling::TileType;
+use crate::world::{apply_movement, Movement, Point};
pub trait Entity {
+ /// Get information about the entity
fn info(&self) -> String;
+ /// Initial placement of the entity
fn place(&mut self, location: Point);
+ /// Get the tiletype for the entity
+ fn get_tiletype(&self) -> &TileType;
+ /// Get the entity's current location
+ fn get_location(&self) -> &Point;
+ /// Get the entity's previous location (before it moved)
+ fn get_previous_location(&self) -> &Point;
+ /// Move the entity to another point
+ fn move_to(&mut self, location: Point);
+ /// Move the entity with a movement differential
+ fn move_by(&mut self, movement: Movement) -> Result<(), String>;
+ /// Know if the entity needs to be re-rendered
+ fn is_dirty(&self) -> bool;
+ /// Declare the entity clean
+ fn clean(&mut self);
}
#[derive(Clone)]
@@ -15,43 +30,26 @@ pub struct Character { pub class: String,
pub health: i32,
pub level: i32,
- pub location: Point,
+ location: Point,
+ previous_location: Point,
+ dirty: bool,
max_health: i32,
attack: i32,
dodge: i32,
luck: i32,
xp: i32,
- tile_type: TileType
-}
-
-pub trait Render {
- fn render(&self, window: &Window);
+ tile_type: TileType,
}
pub trait Enemy {
- fn new(
- class: String,
- health: i32,
- attack: i32,
- dodge: i32,
- luck: i32,
- location: Point
- ) -> Self;
+ fn new(class: String, health: i32, attack: i32, dodge: i32, luck: i32, location: Point)
+ -> Self;
fn set_tile_type(&mut self, tile_type: TileType);
}
pub trait Player {
- fn new(
- name: String,
- class: String,
- health: i32,
- attack: i32,
- dodge: i32,
- luck: i32,
- level: i32,
- location: Point
- ) -> Self;
+ fn new(name: String, class: String, health: i32, attack: i32, dodge: i32, luck: i32) -> Self;
fn damage(&mut self, damage_amount: i32);
fn heal(&mut self, heal_amount: i32);
fn attack(&self) -> i32;
@@ -59,26 +57,11 @@ pub trait Player { fn stats(&self) -> String;
}
-impl Render for Character {
- fn render(&self, window: &Window) {
- // window.mv(window.get_max_y() - 2, 0);
- // window.clrtoeol();
-
- // window.refresh();
-
- // window.addstr(self.character.info() + "\n");
-
- // window.mv(self.character.location.1 as i32,self.character.location.0 as i32);
- // window.refresh();
- // draw_block(&window, self.character.tile_type);
- // window.refresh();
-
- }
-}
-
impl Entity for Character {
fn place(&mut self, location: Point) {
self.location = location;
+ self.previous_location = location;
+ self.dirty = true;
}
fn info(&self) -> String {
@@ -87,6 +70,38 @@ impl Entity for Character { self.class, self.health, self.attack, self.dodge, self.luck
)
}
+
+ fn get_tiletype(&self) -> &TileType {
+ &self.tile_type
+ }
+
+ fn get_location(&self) -> &Point {
+ &self.location
+ }
+
+ fn get_previous_location(&self) -> &Point {
+ &self.previous_location
+ }
+
+ fn move_to(&mut self, location: Point) {
+ self.previous_location = self.location;
+ self.location = location;
+ self.dirty = true;
+ }
+
+ fn move_by(&mut self, movement: Movement) -> Result<(), String> {
+ self.previous_location = self.location;
+ self.location = apply_movement(self.location, movement)?;
+ Ok(())
+ }
+
+ fn is_dirty(&self) -> bool {
+ self.dirty
+ }
+
+ fn clean(&mut self) {
+ self.dirty = false;
+ }
}
impl Enemy for Character {
@@ -96,7 +111,7 @@ impl Enemy for Character { attack: i32,
dodge: i32,
luck: i32,
- location: Point
+ location: Point,
) -> Character {
Character {
name: class.clone(),
@@ -109,7 +124,9 @@ impl Enemy for Character { level: 0,
xp: 0,
location: location,
- tile_type: TileType::Character
+ previous_location: location,
+ tile_type: TileType::Character,
+ dirty: false,
}
}
@@ -126,8 +143,6 @@ impl Player for Character { attack: i32,
dodge: i32,
luck: i32,
- level: i32,
- location: Point
) -> Character {
Character {
name: name,
@@ -139,8 +154,10 @@ impl Player for Character { luck: luck,
xp: 0,
level: 0,
- location: location,
- tile_type: TileType::Player
+ location: (0, 0),
+ previous_location: (0, 0),
+ tile_type: TileType::Player,
+ dirty: false,
}
}
@@ -177,7 +194,7 @@ mod tests { use super::*;
fn test_attack() {
- let bob: Character = Enemy::new("Rogue".to_string(), 1, 4, 1, 4, (0, 0));
+ let bob: Character = Enemy::new(String::from("Rogue"), 1, 4, 1, 4, (0, 0));
assert_eq!(bob.attack(), 6);
}
diff --git a/src/main.rs b/src/main.rs index ab93eb5..3da2a33 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,6 @@ extern crate rand; extern crate pancurses; -#[macro_use] extern crate text_io; mod state; @@ -9,7 +8,7 @@ mod entities; mod world; mod tiling; -use entities::Player; +use entities::{Player, Character, Entity}; use pancurses::{ initscr, endwin, @@ -18,7 +17,7 @@ use pancurses::{ }; use std::env; use state::State; -use world::{Dungeon}; +use world::{Dungeon, LEFT, RIGHT, UP, DOWN}; fn get_player_name() -> String { match env::var_os("USER") { @@ -33,38 +32,44 @@ fn main() { let mut state = State::new( Player::new( get_player_name(), - "Warrior".to_string(), + String::from("Warrior"), 30, 10, 10, - 20, - 1, - (0, 0) + 20 ), - Dungeon::new(window.get_max_x() as usize, window.get_max_y() as usize, 5), + Dungeon::new(window.get_max_x() as usize, (window.get_max_y() - 2) as usize, 5), ); state.init(); window.keypad(true); noecho(); - + + state.render_level(&window); + loop { - // update actors - - state.render_level(&window); + // update + state.render_entities(&window); - // update character - state.show_character(&window); + state.render_player(&window); // get input and execute it match window.getch() { - - Some(Input::Character('h')) => { window.addstr("q: quit\n"); }, - // Some(Input::KeyDown) => { window.addstr("down\n"); }, - // Some(Input::KeyUp) => { window.addch('b'); }, - // Some(Input::KeyLeft) => { window.addch('c'); }, - // Some(Input::KeyRight) => { window.addch('d'); }, + Some(Input::Character('?')) => { window.addstr("q: quit\n"); }, + Some(Input::Character('j')) => { + state.player.move_by(DOWN).unwrap(); + // state.get_player_mut().move_by(DOWN).unwrap(); + }, + Some(Input::Character('k')) => { + state.get_player_mut().move_by(UP).unwrap(); + }, + Some(Input::Character('h')) => { + state.get_player_mut().move_by(LEFT).unwrap(); + }, + Some(Input::Character('l')) => { + state.get_player_mut().move_by(RIGHT).unwrap(); + }, Some(Input::Character('q')) => break, Some(_) => (), None => (), diff --git a/src/state.rs b/src/state.rs index 7553f58..2f09d1e 100644 --- a/src/state.rs +++ b/src/state.rs @@ -1,40 +1,76 @@ use pancurses::Window; -use crate::entities::{Character, Entity, Render}; +use crate::tiling::{TileType, TileGrid, tile_to_str}; +use crate::entities::{Character, Entity}; use crate::world::{Dungeon, Generatable, Level}; pub struct State { pub player: Character, - pub dungeon: Dungeon, - pub level: usize, + dungeon: Dungeon, + level: usize, + grid: Option<TileGrid> +} + +pub fn draw_block(window: &Window, block: &TileType) { + window.printw(tile_to_str(block)); } impl State { - pub fn new( - player: Character, - dungeon: Dungeon, - ) -> Self { + pub fn new(player: Character, dungeon: Dungeon) -> State { State { - player: player, - dungeon: dungeon, + player, + dungeon, level: 0, + grid: None } } pub fn init(&mut self) { self.dungeon.generate(); self.player.place(self.current_level().get_start_point()); + self.switch_level(0); + } + + pub fn switch_level(&mut self, num_level: usize) { + self.level = num_level; + self.grid = Some(self.current_level().to_tilegrid().unwrap()); } pub fn render_level(&self, window: &Window) { - self.current_level().render(window); + for (linenum, line) in self.grid.as_ref().unwrap().raw_data().iter().enumerate() { + for block in line.iter() { + draw_block(&window, &block); + } + window.mv(linenum as i32, 0); + } } - pub fn show_character(&self, window: &Window) { - self.player.render(window); + fn render_entity(&self, entity: &dyn Entity, window: &Window) { + if !entity.is_dirty() { + return; + } + let dirt = entity.get_previous_location(); + window.mv(dirt.1 as i32, dirt.0 as i32); + draw_block(window, self.grid.as_ref().unwrap().get_block_at(dirt.0, dirt.1)); + window.mv(entity.get_location().1 as i32, entity.get_location().0 as i32); + draw_block(window, entity.get_tiletype()); } - fn current_level(&self) -> &Level { + pub fn render_entities(&self, window: &Window) { + for e in self.current_level().entities.iter() { + self.render_entity(&**e, window); + } + } + + pub fn render_player(&self, window: &Window) { + self.render_entity(&self.player, window) + } + + pub fn current_level(&self) -> &Level { &self.dungeon.levels[self.level] } + + pub fn get_player_mut(&mut self) -> &mut Character { + &mut self.player + } }
\ No newline at end of file diff --git a/src/tiling.rs b/src/tiling.rs index fbaae40..9bdb071 100644 --- a/src/tiling.rs +++ b/src/tiling.rs @@ -1,8 +1,5 @@ extern crate pancurses; -use pancurses::{Window}; - - pub struct TileGrid { grid: Vec<Vec<TileType>> } @@ -39,9 +36,13 @@ impl TileGrid { pub fn raw_data(& self) -> & Vec<Vec<TileType>> { &self.grid } + + pub fn get_block_at(&self, x: usize, y: usize) -> &TileType { + &self.grid[y + 1][x] + } } -fn tile_to_str(tile: &TileType) -> &str { +pub fn tile_to_str(tile: &TileType) -> &str { match tile { TileType::Floor => ".", TileType::Wall => "#", @@ -53,10 +54,6 @@ fn tile_to_str(tile: &TileType) -> &str { } } -pub fn draw_block(window: &Window, block: &TileType) { - window.printw(tile_to_str(block)); -} - pub trait Tileable { fn tile(&self, grid: &mut TileGrid) -> Result<(), String>; } diff --git a/src/world.rs b/src/world.rs index 9f58030..c6f0912 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,41 +1,50 @@ +use crate::entities::{Character, Enemy, Entity};
+use crate::tiling::{TileGrid, TileType, Tileable};
use rand::Rng;
-use pancurses::{Window};
-use crate::entities::{Character, Entity, Enemy};
-use crate::tiling::{TileGrid, Tileable, TileType, draw_block};
pub type Point = (usize, usize);
+pub type Movement = (i8, i8);
pub enum Direction {
North,
South,
East,
- West
+ West,
}
enum CorridorType {
Horizontal,
- Vertical
+ Vertical,
}
-const LEFT: (i8, i8) = (-1i8, 0);
-const RIGHT: (i8, i8) = (1i8, 0);
-const UP: (i8, i8) = (0, -1i8);
-const DOWN: (i8, i8) = (0, 1i8);
+pub const LEFT: Movement = (-1, 0);
+pub const RIGHT: Movement = (1, 0);
+pub const UP: Movement = (0, -1);
+pub const DOWN: Movement = (0, 1);
+
+pub fn apply_movement(point: Point, movement: Movement) -> Result<Point, String> {
+ let x = point.0 as i32 + movement.0 as i32;
+ let y = point.1 as i32 + movement.1 as i32;
+ if x < 0 || y < 0 {
+ return Err(String::from("Can't move point off screen"));
+ }
+ Ok((x as usize, y as usize))
+}
struct RoomEdge {
start: Point,
mid_point: Point,
end: Point,
- corridor_dir: (i8, i8)
+ corridor_dir: Movement,
}
impl RoomEdge {
- pub fn new(start: Point, end: Point, corridor_dir: (i8, i8)) -> RoomEdge {
+ pub fn new(start: Point, end: Point, corridor_dir: Movement) -> RoomEdge {
RoomEdge {
start,
end,
mid_point: (end.0 - start.0 / 2, end.1 - start.1 / 2),
- corridor_dir
+ corridor_dir,
}
}
}
@@ -45,7 +54,7 @@ struct Room { center: Point,
width: usize,
height: usize,
- edges: [RoomEdge; 4]
+ edges: [RoomEdge; 4],
}
impl Room {
@@ -54,13 +63,24 @@ impl Room { start,
width,
height,
- center: (start.0 + (width as f32 / 2.0) as usize, start.1 + (height as f32 / 2.0) as usize),
+ center: (
+ start.0 + (width as f32 / 2.0) as usize,
+ start.1 + (height as f32 / 2.0) as usize,
+ ),
edges: [
RoomEdge::new(start, (start.0 + width, start.1), UP),
RoomEdge::new(start, (start.0, start.1 + height), LEFT),
- RoomEdge::new((start.0, start.1 + height), (start.0 + width, start.1 + height), DOWN),
- RoomEdge::new((start.0 + width, start.1), (start.0 + width, start.1), RIGHT)
- ]
+ RoomEdge::new(
+ (start.0, start.1 + height),
+ (start.0 + width, start.1 + height),
+ DOWN,
+ ),
+ RoomEdge::new(
+ (start.0 + width, start.1),
+ (start.0 + width, start.1),
+ RIGHT,
+ ),
+ ],
}
}
}
@@ -96,7 +116,7 @@ impl Tileable for Room { struct Corridor {
start: Point,
length: usize,
- direction: CorridorType
+ direction: CorridorType,
}
impl Corridor {
@@ -104,7 +124,7 @@ impl Corridor { Corridor {
start,
length,
- direction
+ direction,
}
}
@@ -134,7 +154,7 @@ impl Tileable for Corridor { // TODO: ensure the corridor isn't leaving the grid.
match self.direction {
CorridorType::Horizontal => self.tile_horizontal(grid),
- CorridorType::Vertical => self.tile_vertical(grid)
+ CorridorType::Vertical => self.tile_vertical(grid),
}
Ok(())
}
@@ -148,14 +168,14 @@ pub struct Level { corridors: Vec<Corridor>,
pub entities: Vec<Box<dyn Entity>>,
entrance: Point,
- exit: Point
+ exit: Point,
}
pub struct Dungeon {
xsize: usize,
ysize: usize,
depth: usize,
- pub levels: Vec<Level>
+ pub levels: Vec<Level>,
}
pub trait Generatable {
@@ -172,11 +192,7 @@ fn ver_dist(point1: Point, point2: Point) -> f32 { /// The distance between 2 points
fn distance(point1: Point, point2: Point) -> f32 {
- (
- hor_dist(point1, point2).powf(2.0)
- +
- ver_dist(point1, point2).powf(2.0)
- ).sqrt()
+ (hor_dist(point1, point2).powf(2.0) + ver_dist(point1, point2).powf(2.0)).sqrt()
}
impl Dungeon {
@@ -185,7 +201,7 @@ impl Dungeon { xsize,
ysize,
depth,
- levels: vec![]
+ levels: vec![],
}
}
}
@@ -219,14 +235,14 @@ impl Level { entities: vec![],
entrance: match start {
Some(st) => st,
- None => (0, 0)
+ None => (0, 0),
},
exit: (0, 0),
- depth
+ depth,
}
}
- fn to_tilegrid(&self) -> Result<TileGrid, String> {
+ pub fn to_tilegrid(&self) -> Result<TileGrid, String> {
let mut grid = TileGrid::new(self.xsize, self.ysize);
for room in &self.rooms {
@@ -247,18 +263,7 @@ impl Level { if self.rooms.len() > 0 {
return self.rooms[0].center;
}
- return (0,0)
- }
-
- pub fn render(&self, window: &Window) {
- let grid = self.to_tilegrid().unwrap();
-
- for (linenum, line) in grid.raw_data().iter().enumerate() {
- for block in line.iter() {
- draw_block(&window, &block);
- }
- window.mv(linenum as i32, 0);
- }
+ return (0, 0);
}
pub fn get_entrance(&self) -> Point {
@@ -271,10 +276,11 @@ impl Level { fn overlaps(&self, start: Point, width: usize, height: usize, padding: usize) -> bool {
for room in &self.rooms {
- if room.start.0 < start.0 + width + padding &&
- room.start.0 + room.width + padding > start.0 &&
- room.start.1 < start.1 + height + padding &&
- room.start.1 + room.height + padding > start.1 {
+ if room.start.0 < start.0 + width + padding
+ && room.start.0 + room.width + padding > start.0
+ && room.start.1 < start.1 + height + padding
+ && room.start.1 + room.height + padding > start.1
+ {
return true;
}
}
@@ -283,14 +289,17 @@ impl Level { }
fn room_distances(&self, point: Point) -> Vec<(usize, f32)> {
- let mut dists: Vec<(usize, f32)> = self.rooms
+ let mut dists: Vec<(usize, f32)> = self
+ .rooms
.iter()
.enumerate()
.map(|(room_num, room): (usize, &Room)| -> (usize, f32) {
(room_num, distance(point, room.center))
})
.collect();
- dists.sort_by(|(_, dista): &(usize, f32), (_, distb): &(usize, f32)| dista.partial_cmp(&distb).unwrap());
+ dists.sort_by(|(_, dista): &(usize, f32), (_, distb): &(usize, f32)| {
+ dista.partial_cmp(&distb).unwrap()
+ });
dists
}
@@ -303,13 +312,13 @@ impl Level { // TODO: Find a way to write a lambda to generate the start point.
let mut start: Point = (
rng.gen_range(0, self.xsize - room_width),
- rng.gen_range(0, self.ysize - room_height)
+ rng.gen_range(0, self.ysize - room_height),
);
while self.overlaps(start, room_width, room_height, 2) {
start = (
rng.gen_range(0, self.xsize - room_width),
- rng.gen_range(0, self.ysize - room_height)
+ rng.gen_range(0, self.ysize - room_height),
);
}
@@ -323,7 +332,7 @@ impl Level { let start = (
(center.0 as f32 - (room_width as f32 / 2f32)).round() as usize,
- (center.1 as f32 - (room_height as f32 / 2f32)).round() as usize
+ (center.1 as f32 - (room_height as f32 / 2f32)).round() as usize,
);
Room::new(start, room_width, room_height)
@@ -359,7 +368,7 @@ impl Generatable for Level { self.corridors.push(Corridor::new(
xorigin,
xlength.abs() as usize,
- CorridorType::Horizontal
+ CorridorType::Horizontal,
));
let angle_point = (xorigin.0 + xlength.abs() as usize, xorigin.1);
@@ -376,7 +385,7 @@ impl Generatable for Level { self.corridors.push(Corridor::new(
yorigin,
ylength.abs() as usize,
- CorridorType::Vertical
+ CorridorType::Vertical,
));
}
@@ -395,12 +404,15 @@ impl Generatable for Level { // Create the enemy
self.entities.push(Box::<Character>::new(Enemy::new(
- "snake".to_string(),
+ String::from("snake"),
2 * self.depth as i32,
(2.0 * self.depth as f32 * 0.6).round() as i32,
(20.0 * self.depth as f32 * 0.2).max(80.0).round() as i32,
0,
- (room.start.0 + rng.gen_range(0, room.width), room.start.1 + rng.gen_range(0, room.height))
+ (
+ room.start.0 + rng.gen_range(0, room.width),
+ room.start.1 + rng.gen_range(0, room.height),
+ ),
)));
}
}
|