From d6bda2f1662c082b98ac5f05ac9acb5838c1db67 Mon Sep 17 00:00:00 2001 From: Guillaume Pasquet Date: Wed, 13 Nov 2019 13:46:46 +0100 Subject: Better entities --- src/entities.rs | 81 +++++++++++++++++++++++++++-------------- src/main.rs | 17 +++++---- src/world.rs | 110 ++++++++++++++++++++++++++++++++++---------------------- 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 { + 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, pub entities: Vec>, entrance: Point, - exit: Point + exit: Point, } pub struct Dungeon { xsize: usize, ysize: usize, depth: usize, - pub levels: Vec + pub levels: Vec, } 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::::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), + ), ))); } } -- cgit v1.2.3