aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuillaume Pasquet <guillaume.pasquet@eggplant.io>2019-11-13 13:46:46 +0100
committerGuillaume Pasquet <guillaume.pasquet@eggplant.io>2019-11-13 13:46:46 +0100
commitd6bda2f1662c082b98ac5f05ac9acb5838c1db67 (patch)
tree09db50cafd8c200f0b1c5df9ed7a3c6db1d63fd9
parent53e72c91a743b34eda2db77b69f593e15361623b (diff)
Better entities
-rw-r--r--src/entities.rs81
-rw-r--r--src/main.rs17
-rw-r--r--src/world.rs110
3 files changed, 130 insertions, 78 deletions
diff --git a/src/entities.rs b/src/entities.rs
index 50841ab..d462fa9 100644
--- a/src/entities.rs
+++ b/src/entities.rs
@@ -1,14 +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)]
@@ -17,38 +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
+ 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
- ) -> 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,6 +60,8 @@ pub trait Player {
impl Entity for Character {
fn place(&mut self, location: Point) {
self.location = location;
+ self.previous_location = location;
+ self.dirty = true;
}
fn info(&self) -> String {
@@ -75,6 +78,29 @@ impl Entity for Character {
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 = apply_movement(self.location, movement)?;
+ Ok(())
+ }
+
+ fn is_dirty(&self) -> bool {
+ self.dirty
+ }
+
+ fn clean(&mut self) {
+ self.dirty = false;
+ }
}
impl Enemy for Character {
@@ -84,7 +110,7 @@ impl Enemy for Character {
attack: i32,
dodge: i32,
luck: i32,
- location: Point
+ location: Point,
) -> Character {
Character {
name: class.clone(),
@@ -97,7 +123,9 @@ impl Enemy for Character {
level: 0,
xp: 0,
location: location,
- tile_type: TileType::Character
+ previous_location: location,
+ tile_type: TileType::Character,
+ dirty: false,
}
}
@@ -114,7 +142,6 @@ impl Player for Character {
attack: i32,
dodge: i32,
luck: i32,
- level: i32
) -> Character {
Character {
name: name,
@@ -127,7 +154,9 @@ impl Player for Character {
xp: 0,
level: 0,
location: (0, 0),
- tile_type: TileType::Player
+ previous_location: (0, 0),
+ tile_type: TileType::Player,
+ dirty: false,
}
}
@@ -164,7 +193,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 599c65c..9f5a432 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -24,13 +24,12 @@ fn main() {
let mut state = State::new(
Player::new(
- "Kshar".to_string(),
- "Warrior".to_string(),
+ String::from("Kshar"),
+ String::from("Warrior"),
30,
10,
10,
- 20,
- 1
+ 20
),
Dungeon::new(window.get_max_x() as usize, (window.get_max_y() - 2) as usize, 5),
);
@@ -50,11 +49,11 @@ fn main() {
// 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')) => { window.addstr("down\n"); },
+ Some(Input::Character('k')) => { window.addstr("up\n"); },
+ Some(Input::Character('h')) => { window.addstr("left\n"); },
+ Some(Input::Character('l')) => { window.addstr("right\n"); },
Some(Input::Character('q')) => break,
Some(_) => (),
None => (),
diff --git a/src/world.rs b/src/world.rs
index 7652157..73fc802 100644
--- a/src/world.rs
+++ b/src/world.rs
@@ -1,40 +1,50 @@
+use crate::entities::{Character, Enemy, Entity};
+use crate::tiling::{TileGrid, TileType, Tileable};
use rand::Rng;
-use crate::entities::{Character, Entity, Enemy};
-use crate::tiling::{TileGrid, Tileable, TileType};
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);
+const LEFT: Movement = (-1, 0);
+const RIGHT: Movement = (1, 0);
+const UP: Movement = (0, -1);
+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,
}
}
}
@@ -44,7 +54,7 @@ struct Room {
center: Point,
width: usize,
height: usize,
- edges: [RoomEdge; 4]
+ edges: [RoomEdge; 4],
}
impl Room {
@@ -53,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,
+ ),
+ ],
}
}
}
@@ -95,7 +116,7 @@ impl Tileable for Room {
struct Corridor {
start: Point,
length: usize,
- direction: CorridorType
+ direction: CorridorType,
}
impl Corridor {
@@ -103,7 +124,7 @@ impl Corridor {
Corridor {
start,
length,
- direction
+ direction,
}
}
@@ -133,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(())
}
@@ -147,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 {
@@ -171,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 {
@@ -184,7 +201,7 @@ impl Dungeon {
xsize,
ysize,
depth,
- levels: vec![]
+ levels: vec![],
}
}
}
@@ -218,10 +235,10 @@ impl Level {
entities: vec![],
entrance: match start {
Some(st) => st,
- None => (0, 0)
+ None => (0, 0),
},
exit: (0, 0),
- depth
+ depth,
}
}
@@ -246,7 +263,7 @@ impl Level {
if self.rooms.len() > 0 {
return self.rooms[0].center;
}
- return (0,0)
+ return (0, 0);
}
pub fn get_entrance(&self) -> Point {
@@ -259,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;
}
}
@@ -271,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
}
@@ -291,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),
);
}
@@ -311,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)
@@ -347,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);
@@ -364,7 +385,7 @@ impl Generatable for Level {
self.corridors.push(Corridor::new(
yorigin,
ylength.abs() as usize,
- CorridorType::Vertical
+ CorridorType::Vertical,
));
}
@@ -383,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),
+ ),
)));
}
}