aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIago Garrido <iago086@gmail.com>2019-11-13 19:21:00 +0100
committerIago Garrido <iago086@gmail.com>2019-11-13 19:21:00 +0100
commitb3d2d5811f3d1c153583695c1f338092ca0db20f (patch)
treee1fec16189079e267c9d752a274dee35838cdcc7
parentc20f1310fb3417cb1b0bf41e2d31db373751e392 (diff)
parent3fb8f5651144ef21338c09c14cd3db03c6416136 (diff)
Merge remote-tracking branch 'upstream/master'
-rw-r--r--img/screenshot.pngbin3212 -> 225307 bytes
-rw-r--r--src/entities.rs117
-rw-r--r--src/main.rs45
-rw-r--r--src/state.rs62
-rw-r--r--src/tiling.rs13
-rw-r--r--src/world.rs124
6 files changed, 214 insertions, 147 deletions
diff --git a/img/screenshot.png b/img/screenshot.png
index 2fe5cbd..ff8ae77 100644
--- a/img/screenshot.png
+++ b/img/screenshot.png
Binary files differ
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),
+ ),
)));
}
}