diff options
-rw-r--r-- | src/animation.rs | 81 | ||||
-rw-r--r-- | src/entity.rs | 101 | ||||
-rw-r--r-- | src/lib.rs | 2 | ||||
-rw-r--r-- | src/npc.rs | 92 | ||||
-rw-r--r-- | src/player.rs | 225 | ||||
-rw-r--r-- | src/state.rs | 19 |
6 files changed, 289 insertions, 231 deletions
diff --git a/src/animation.rs b/src/animation.rs new file mode 100644 index 0000000..24932b2 --- /dev/null +++ b/src/animation.rs @@ -0,0 +1,81 @@ +use ggez::graphics::Rect; +use std::collections::HashMap; +use std::f32::consts::PI; +use std::time::Instant; + +use crate::entity::Action; +use crate::tileset::Tileset; + +#[derive(Clone)] +pub struct Animation { + pub animation: Vec<(usize, Rect)>, + pub animations: HashMap<Action, Vec<(usize, Rect)>>, + pub timer: Instant, + pub source: Rect, +} + +impl Animation { + pub fn new(tileset: &Tileset) -> Animation { + let mut animations = HashMap::new(); + + let mut source = tileset.get_tile_by_entity_keyframe("player-top", 0); + source.h += tileset.get_tile_by_entity_keyframe("player-bottom", 0).h; + animations.insert(Action::IdleLeft, vec![(1, source)]); + + let mut moving = tileset.get_tile_by_entity_keyframe("player-top", 1); + moving.h += tileset.get_tile_by_entity_keyframe("player-bottom", 1).h; + + animations.insert(Action::MovingLeft, vec![(100, source), (100, moving)]); + animations.insert(Action::MovingUpLeft, vec![(100, source), (100, moving)]); + animations.insert(Action::MovingDownLeft, vec![(100, source), (100, moving)]); + + source = flip(source); + moving = flip(moving); + + animations.insert(Action::IdleRight, vec![(1, source)]); + + animations.insert(Action::MovingRight, vec![(100, source), (100, moving)]); + animations.insert(Action::MovingUpRight, vec![(100, source), (100, moving)]); + animations.insert(Action::MovingDownRight, vec![(100, source), (100, moving)]); + + Animation { + animations, + source, + timer: Instant::now(), + animation: Vec::new(), + } + } + + pub fn update(&mut self, action: &Action) { + self.animation = self.animations.get(&action).cloned().unwrap_or_default(); + let (source, timer) = next_source(self.source, &self.animation, self.timer); + self.source = source; + self.timer = timer; + } +} + +pub fn next_source(source: Rect, animation: &[(usize, Rect)], timer: Instant) -> (Rect, Instant) { + if let Some(mut i) = animation.iter().position(|a| a.1 == source) { + if timer.elapsed().as_millis() > animation[i].0 as u128 { + i = if i == animation.len() - 1 { 0 } else { i + 1 }; + (animation[i].1, Instant::now()) + } else { + (source, timer) + } + } else if !animation.is_empty() { + (animation[0].1, timer) + } else { + (source, timer) + } +} + +pub fn convert_angle_to_rad(angle: f32) -> f32 { + angle * (PI / 180.0) +} + +pub fn flip(rect: Rect) -> Rect { + let mut r = rect; + r.x *= -1.0; + r.x -= rect.w; + r +} diff --git a/src/entity.rs b/src/entity.rs index 1c0aa35..8086b77 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -1,91 +1,40 @@ -use ggez::graphics::{spritebatch::SpriteBatch, DrawParam, Rect}; -use ggez::nalgebra::{distance, Point2, Vector2}; -use std::time::Instant; +use ggez::graphics::spritebatch::SpriteBatch; +use ggez::nalgebra::Point2; -use crate::constants; -use crate::map::Map; -use crate::math::random_nearby_point; -use crate::tileset::Tileset; +pub trait Operable { + fn update(&mut self); + fn draw(&self, spritebatch: &mut SpriteBatch); +} #[derive(Clone)] pub struct Entity { - behavior: Behavior, - position: Point2<f32>, - source: Rect, - spawn: Point2<f32>, + pub position: Point2<f32>, + pub spawn: Point2<f32>, + pub action: Action, + pub map_dimensions: (f32, f32), } impl Entity { - pub fn new(tileset: &Tileset, spawn: Point2<f32>) -> Entity { - let mut source = tileset.get_tile_by_entity_keyframe("player-top", 0); - source.h += tileset.get_tile_by_entity_keyframe("player-bottom", 0).h; - + pub fn new(spawn: Point2<f32>, map_dimensions: (f32, f32)) -> Entity { Entity { spawn, - source, + action: Action::IdleLeft, position: spawn, - behavior: Behavior::Wandering(None), - } - } - - pub fn update(&mut self) { - match self.behavior { - Behavior::Wandering(destination) => self.wandering(destination), - Behavior::Waiting(time) => (), - } - } - - pub fn wandering(&mut self, destination: Option<Point2<f32>>) { - match destination { - Some(destination) => { - if distance(&self.position, &destination) < constants::GOAL_DISTANCE { - self.behavior = Behavior::Waiting(Instant::now()) - } else { - if self.position.x < destination.x { - self.position.x += constants::ENTITY_SPEED; - } else { - self.position.x -= constants::ENTITY_SPEED; - } - if self.position.y < destination.y { - self.position.y += constants::ENTITY_SPEED; - } else { - self.position.y -= constants::ENTITY_SPEED; - } - } - } - None => { - self.behavior = Behavior::Wandering(Some(random_nearby_point( - self.spawn, - constants::WANDER_DISTANCE, - ))) - } - } - } - - pub fn waiting(&mut self) {} - - pub fn draw(&self, spritebatch: &mut SpriteBatch) { - let draw_param = DrawParam::default() - .src(self.source) - .dest(self.position) - .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE)); - - spritebatch.add(draw_param); - } - - pub fn build_entities(tileset: &Tileset, map: &Map) -> Vec<Entity> { - let mut entities = Vec::new(); - - for (_name, position) in map.get_spawns() { - entities.push(Entity::new(tileset, position)); + map_dimensions, } - - entities } } -#[derive(Clone)] -enum Behavior { - Waiting(Instant), - Wandering(Option<Point2<f32>>), +#[derive(Clone, Hash, Eq, PartialEq)] +pub enum Action { + IdleLeft, + IdleRight, + MovingUp, + MovingDown, + MovingLeft, + MovingRight, + MovingUpLeft, + MovingUpRight, + MovingDownLeft, + MovingDownRight, } @@ -1,9 +1,11 @@ +pub mod animation; pub mod camera; pub mod constants; pub mod entity; pub mod layer; pub mod map; pub mod math; +pub mod npc; pub mod player; pub mod property; pub mod state; diff --git a/src/npc.rs b/src/npc.rs new file mode 100644 index 0000000..96cb489 --- /dev/null +++ b/src/npc.rs @@ -0,0 +1,92 @@ +use ggez::graphics::{spritebatch::SpriteBatch, DrawParam}; +use ggez::nalgebra::{/*distance,*/ Point2, Vector2}; +//use std::time::Instant; + +use crate::animation::Animation; +use crate::constants; +use crate::entity::{Entity, Operable}; +use crate::map::Map; +use crate::tileset::Tileset; + +#[derive(Clone)] +pub struct NPC { + entity: Entity, + behavior: Behavior, + animation: Animation, +} + +impl Operable for NPC { + fn draw(&self, spritebatch: &mut SpriteBatch) { + spritebatch.add( + DrawParam::default() + .src(self.animation.source) + .dest(self.entity.position) + .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE)), + ); + } + + fn update(&mut self) { + /* + match self.behavior { + Behavior::Wandering(destination) => self.wandering(destination), + Behavior::Waiting(time) => (), + } + */ + self.animation.update(&self.entity.action); + } +} + +impl NPC { + pub fn new(tileset: &Tileset, spawn: Point2<f32>, map_dimensions: (f32, f32)) -> NPC { + NPC { + entity: Entity::new(spawn, map_dimensions), + behavior: Behavior::Wandering(None), + animation: Animation::new(tileset), + } + } + + /*fn wandering(&mut self, destination: Option<Point2<f32>>) { + match destination { + Some(destination) => { + if distance(&self.position, &destination) < constants::GOAL_DISTANCE { + self.behavior = Behavior::Waiting(Instant::now()) + } else { + if self.position.x < destination.x { + self.position.x += constants::ENTITY_SPEED; + } else { + self.position.x -= constants::ENTITY_SPEED; + } + if self.position.y < destination.y { + self.position.y += constants::ENTITY_SPEED; + } else { + self.position.y -= constants::ENTITY_SPEED; + } + } + } + None => { + self.behavior = Behavior::Wandering(Some(random_nearby_point( + self.spawn, + constants::WANDER_DISTANCE, + ))) + } + } + }*/ + + //fn waiting(&mut self) {} + + pub fn build_npcs(tileset: &Tileset, map: &Map) -> Vec<NPC> { + let mut npcs = Vec::new(); + + for (_name, position) in map.get_spawns() { + npcs.push(NPC::new(tileset, position, map.get_dimensions())); + } + + npcs + } +} + +#[derive(Clone)] +enum Behavior { + //Waiting(Instant), + Wandering(Option<Point2<f32>>), +} diff --git a/src/player.rs b/src/player.rs index 6440587..ae43d80 100644 --- a/src/player.rs +++ b/src/player.rs @@ -1,206 +1,139 @@ use ggez::event::KeyCode; -use ggez::graphics::{spritebatch::SpriteBatch, DrawParam, Rect}; +use ggez::graphics::{spritebatch::SpriteBatch, DrawParam}; use ggez::nalgebra::{Point2, Vector2}; -use std::collections::HashMap; -use std::time::Instant; +use crate::animation::Animation; use crate::constants; -use crate::math::{flip, next_source}; +use crate::entity::{Action, Entity, Operable}; use crate::tileset::Tileset; pub struct Player { - pub position: Point2<f32>, - state: AnimationState, - source: Rect, - timer: Instant, - animation: Vec<(usize, Rect)>, - animations: HashMap<AnimationState, Vec<(usize, Rect)>>, - map_height: f32, - map_width: f32, + entity: Entity, + animation: Animation, } -impl Player { - pub fn new(tileset: &Tileset, dimensions: (f32, f32)) -> Player { - Player { - position: Point2::new(0.0, 0.0), - state: AnimationState::IdleLeft, - source: Rect::zero(), - timer: Instant::now(), - animation: Vec::new(), - animations: Player::build_animations(tileset), - map_width: dimensions.0, - map_height: dimensions.1, - } - } - - fn build_animations(tileset: &Tileset) -> HashMap<AnimationState, Vec<(usize, Rect)>> { - let mut animations = HashMap::new(); - - let mut source = tileset.get_tile_by_entity_keyframe("player-top", 0); - source.h += tileset.get_tile_by_entity_keyframe("player-bottom", 0).h; - animations.insert(AnimationState::IdleLeft, vec![(1, source)]); - - let mut moving = tileset.get_tile_by_entity_keyframe("player-top", 1); - moving.h += tileset.get_tile_by_entity_keyframe("player-bottom", 1).h; - - animations.insert(AnimationState::MovingLeft, vec![(100, source), (100, moving)]); - animations.insert( - AnimationState::MovingUpLeft, - vec![(100, source), (100, moving)], - ); - animations.insert( - AnimationState::MovingDownLeft, - vec![(100, source), (100, moving)], - ); - - source = flip(source); - moving = flip(moving); - - animations.insert(AnimationState::IdleRight, vec![(1, source)]); - - animations.insert(AnimationState::MovingRight, vec![(100, source), (100, moving)]); - animations.insert( - AnimationState::MovingUpRight, - vec![(100, source), (100, moving)], +impl Operable for Player { + fn draw(&self, spritebatch: &mut SpriteBatch) { + spritebatch.add( + DrawParam::default() + .src(self.animation.source) + .dest(self.entity.position) + .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE)), ); - animations.insert( - AnimationState::MovingDownRight, - vec![(100, source), (100, moving)], - ); - - animations } - pub fn draw(&self, spritebatch: &mut SpriteBatch) { - let draw_param = DrawParam::default() - .src(self.source) - .dest(self.position) - .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE)); - - spritebatch.add(draw_param); + fn update(&mut self) { + self.move_position(); + self.animation.update(&self.entity.action); } +} - pub fn update(&mut self) { - self.move_position(); +impl Player { + pub fn new(tileset: &Tileset, dimensions: (f32, f32)) -> Player { + Player { + entity: Entity::new(Point2::new(0.0, 0.0), dimensions), + animation: Animation::new(tileset), + } + } - self.animation = self - .animations - .get(&self.state) - .cloned() - .unwrap_or_default(); - let (source, timer) = next_source(self.source, &self.animation, self.timer); - self.source = source; - self.timer = timer; + pub fn get_position(&self) -> Point2<f32> { + self.entity.position } fn move_position(&mut self) { - match self.state { - AnimationState::MovingUp => self.position.y -= constants::PLAYER_SPEED, - AnimationState::MovingUpLeft => { - self.position.x -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); - self.position.y -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); + match self.entity.action { + Action::MovingUp => self.entity.position.y -= constants::PLAYER_SPEED, + Action::MovingUpLeft => { + self.entity.position.x -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); + self.entity.position.y -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); } - AnimationState::MovingUpRight => { - self.position.x += constants::PLAYER_SPEED / 2.0_f32.sqrt(); - self.position.y -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); + Action::MovingUpRight => { + self.entity.position.x += constants::PLAYER_SPEED / 2.0_f32.sqrt(); + self.entity.position.y -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); } - AnimationState::MovingLeft => self.position.x -= constants::PLAYER_SPEED, - AnimationState::MovingDown => self.position.y += constants::PLAYER_SPEED, - AnimationState::MovingDownLeft => { - self.position.x -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); - self.position.y += constants::PLAYER_SPEED / 2.0_f32.sqrt(); + Action::MovingLeft => self.entity.position.x -= constants::PLAYER_SPEED, + Action::MovingDown => self.entity.position.y += constants::PLAYER_SPEED, + Action::MovingDownLeft => { + self.entity.position.x -= constants::PLAYER_SPEED / 2.0_f32.sqrt(); + self.entity.position.y += constants::PLAYER_SPEED / 2.0_f32.sqrt(); } - AnimationState::MovingDownRight => { - self.position.x += constants::PLAYER_SPEED / 2.0_f32.sqrt(); - self.position.y += constants::PLAYER_SPEED / 2.0_f32.sqrt(); + Action::MovingDownRight => { + self.entity.position.x += constants::PLAYER_SPEED / 2.0_f32.sqrt(); + self.entity.position.y += constants::PLAYER_SPEED / 2.0_f32.sqrt(); } - AnimationState::MovingRight => self.position.x += constants::PLAYER_SPEED, - AnimationState::IdleLeft | AnimationState::IdleRight => (), + Action::MovingRight => self.entity.position.x += constants::PLAYER_SPEED, + Action::IdleLeft | Action::IdleRight => (), } let pixel_width = constants::TILE_WIDTH * constants::TILE_SCALE; let pixel_height = constants::TILE_HEIGHT * constants::TILE_SCALE; - if self.position.x < 0.0 { - self.position.x = 0.0; - } else if self.position.x + pixel_height > self.map_width { - self.position.x = self.map_width - pixel_width; + if self.entity.position.x < 0.0 { + self.entity.position.x = 0.0; + } else if self.entity.position.x + pixel_height > self.entity.map_dimensions.0 { + self.entity.position.x = self.entity.map_dimensions.0 - pixel_width; } - if self.position.y < 0.0 { - self.position.y = 0.0; - } else if self.position.y + pixel_height > self.map_height { - self.position.y = self.map_height - pixel_height; + if self.entity.position.y < 0.0 { + self.entity.position.y = 0.0; + } else if self.entity.position.y + pixel_height > self.entity.map_dimensions.1 { + self.entity.position.y = self.entity.map_dimensions.1 - pixel_height; } } pub fn give_key_down(&mut self, keycode: KeyCode) { - let original_state = self.state.clone(); + let original_state = self.entity.action.clone(); - self.state = match keycode { + self.entity.action = match keycode { KeyCode::W => match original_state { - AnimationState::MovingLeft => AnimationState::MovingUpLeft, - AnimationState::MovingRight => AnimationState::MovingUpRight, - _ => AnimationState::MovingUp, + Action::MovingLeft => Action::MovingUpLeft, + Action::MovingRight => Action::MovingUpRight, + _ => Action::MovingUp, }, KeyCode::A => match original_state { - AnimationState::MovingUp => AnimationState::MovingUpLeft, - AnimationState::MovingDown => AnimationState::MovingDownLeft, - _ => AnimationState::MovingLeft, + Action::MovingUp => Action::MovingUpLeft, + Action::MovingDown => Action::MovingDownLeft, + _ => Action::MovingLeft, }, KeyCode::S => match original_state { - AnimationState::MovingLeft => AnimationState::MovingDownLeft, - AnimationState::MovingRight => AnimationState::MovingDownRight, - _ => AnimationState::MovingDown, + Action::MovingLeft => Action::MovingDownLeft, + Action::MovingRight => Action::MovingDownRight, + _ => Action::MovingDown, }, KeyCode::D => match original_state { - AnimationState::MovingUp => AnimationState::MovingUpRight, - AnimationState::MovingDown => AnimationState::MovingDownRight, - _ => AnimationState::MovingRight, + Action::MovingUp => Action::MovingUpRight, + Action::MovingDown => Action::MovingDownRight, + _ => Action::MovingRight, }, _ => original_state, } } pub fn give_key_up(&mut self, keycode: KeyCode) { - let original_state = self.state.clone(); + let original_state = self.entity.action.clone(); - self.state = match keycode { + self.entity.action = match keycode { KeyCode::W => match original_state { - AnimationState::MovingUpLeft => AnimationState::MovingLeft, - AnimationState::MovingUpRight => AnimationState::MovingRight, - _ => AnimationState::IdleLeft, + Action::MovingUpLeft => Action::MovingLeft, + Action::MovingUpRight => Action::MovingRight, + _ => Action::IdleLeft, }, KeyCode::A => match original_state { - AnimationState::MovingUpLeft => AnimationState::MovingUp, - AnimationState::MovingDownLeft => AnimationState::MovingDown, - _ => AnimationState::IdleLeft, + Action::MovingUpLeft => Action::MovingUp, + Action::MovingDownLeft => Action::MovingDown, + _ => Action::IdleLeft, }, KeyCode::S => match original_state { - AnimationState::MovingDownLeft => AnimationState::MovingLeft, - AnimationState::MovingDownRight => AnimationState::MovingRight, - _ => AnimationState::IdleRight, + Action::MovingDownLeft => Action::MovingLeft, + Action::MovingDownRight => Action::MovingRight, + _ => Action::IdleRight, }, KeyCode::D => match original_state { - AnimationState::MovingUpRight => AnimationState::MovingUp, - AnimationState::MovingDownRight => AnimationState::MovingDown, - _ => AnimationState::IdleRight, + Action::MovingUpRight => Action::MovingUp, + Action::MovingDownRight => Action::MovingDown, + _ => Action::IdleRight, }, _ => original_state, } } } - -#[derive(Clone, Hash, Eq, PartialEq)] -enum AnimationState { - IdleLeft, - IdleRight, - MovingUp, - MovingDown, - MovingLeft, - MovingRight, - MovingUpLeft, - MovingUpRight, - MovingDownLeft, - MovingDownRight, -} diff --git a/src/state.rs b/src/state.rs index a541a02..b705fd4 100644 --- a/src/state.rs +++ b/src/state.rs @@ -3,8 +3,9 @@ use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Imag use ggez::{filesystem, Context, GameResult}; use crate::camera::Camera; -use crate::entity::Entity; +use crate::entity::Operable; use crate::map::Map; +use crate::npc::NPC; use crate::player::Player; use crate::tileset::Tileset; @@ -13,7 +14,7 @@ pub struct State { spritebatch: SpriteBatch, camera: Camera, player: Player, - entities: Vec<Entity>, + npcs: Vec<NPC>, } impl State { @@ -32,7 +33,7 @@ impl State { spritebatch: SpriteBatch::new(image), camera: Camera::new(context, map_dimensions), player: Player::new(&tileset, map_dimensions), - entities: Entity::build_entities(&tileset, &map), + npcs: NPC::build_npcs(&tileset, &map), }) } } @@ -41,10 +42,11 @@ impl EventHandler for State { fn update(&mut self, _context: &mut Context) -> GameResult { self.map.update(); self.player.update(); - for entity in self.entities.iter_mut() { - entity.update(); + for npc in self.npcs.iter_mut() { + npc.update(); } - self.camera.give_center(self.player.position); + + self.camera.give_center(self.player.get_position()); Ok(()) } @@ -53,9 +55,8 @@ impl EventHandler for State { self.map.draw(&mut self.spritebatch); self.player.draw(&mut self.spritebatch); - - for entity in &self.entities { - entity.draw(&mut self.spritebatch); + for npc in self.npcs.iter_mut() { + npc.draw(&mut self.spritebatch); } graphics::draw( |