diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/constants.rs | 2 | ||||
-rw-r--r-- | src/dialogbox.rs | 66 | ||||
-rw-r--r-- | src/entity.rs | 2 | ||||
-rw-r--r-- | src/game.rs | 81 | ||||
-rw-r--r-- | src/lib.rs | 1 | ||||
-rw-r--r-- | src/main.rs | 6 | ||||
-rw-r--r-- | src/map.rs | 5 | ||||
-rw-r--r-- | src/npc.rs | 61 | ||||
-rw-r--r-- | src/player.rs | 3 | ||||
-rw-r--r-- | src/world.rs | 119 |
10 files changed, 244 insertions, 102 deletions
diff --git a/src/constants.rs b/src/constants.rs index 8a873c6..041d210 100644 --- a/src/constants.rs +++ b/src/constants.rs @@ -4,7 +4,7 @@ pub const TILE_SCALE: f32 = 2.75; pub const PLAYER_SPEED: f32 = 3.0; pub const WANDER_DISTANCE: f32 = 200.0; -pub const GOAL_DISTANCE: f32 = 10.0; +pub const INTERACT_DISTANCE: f32 = 30.0; pub const WAIT_TIME: u64 = 3; pub const FLOAT_PRECISION: f32 = 0.001; diff --git a/src/dialogbox.rs b/src/dialogbox.rs index 1f6415b..4bdce04 100644 --- a/src/dialogbox.rs +++ b/src/dialogbox.rs @@ -3,28 +3,53 @@ use ggez::graphics::{ self, DrawMode, DrawParam, Font, Mesh, MeshBuilder, Rect, Scale, Text, TextFragment, }; use ggez::nalgebra::Point2; -use ggez::{Context, GameResult}; +use ggez::{filesystem, Context, GameResult}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; use crate::constants; +use crate::npc::Character; +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Dialog { + text: String, + responses: Vec<(usize, String)>, +} + +#[derive(Clone, Debug, Default)] +pub struct DialogTree { + dialogs: HashMap<usize, Dialog>, +} + +impl DialogTree { + pub fn new(context: &mut Context, character: Character) -> DialogTree { + DialogTree { + dialogs: serde_json::from_reader( + filesystem::open(context, "/dialogtrees/".to_string() + character.to_str()) + .unwrap(), + ) + .unwrap(), + } + } +} + +#[derive(Clone)] pub struct DialogBox { + dialogtree: Option<DialogTree>, + font: Font, + text: Option<Text>, mesh: Mesh, - text: Text, conf: Conf, - pub visible: bool, } impl DialogBox { pub fn new(context: &mut Context) -> DialogBox { let conf = Conf::new(); - let font = Font::new(context, "/fonts/SONORM__.ttf").unwrap(); DialogBox { - text: Text::new( - TextFragment::new("Ave !") - .font(font) - .scale(Scale::uniform(40.0)), - ), + dialogtree: None, + font: Font::new(context, "/fonts/SONORM__.ttf").unwrap(), + text: None, mesh: MeshBuilder::new() .rectangle( DrawMode::fill(), @@ -38,17 +63,30 @@ impl DialogBox { ) .build(context) .unwrap(), - visible: false, conf, } } + pub fn update(&mut self) { + if let Some(dialogtree) = &self.dialogtree { + if self.text.is_none() { + self.text = Some(Text::new( + TextFragment::new(dialogtree.dialogs.get(&0).unwrap().text.as_str()) + .font(self.font) + .scale(Scale::uniform(40.0)), + )); + } + } else { + self.text = None; + } + } + pub fn draw(&self, context: &mut Context) -> GameResult { - if self.visible { + if let Some(text) = &self.text { graphics::draw(context, &self.mesh, DrawParam::default())?; graphics::draw( context, - &self.text, + text, DrawParam::default().dest(Point2::new( self.conf.window_mode.width * 0.11, 2.6 * self.conf.window_mode.height / 4.0, @@ -58,4 +96,8 @@ impl DialogBox { Ok(()) } + + pub fn give_dialogtree(&mut self, dialogtree: Option<DialogTree>) { + self.dialogtree = dialogtree; + } } diff --git a/src/entity.rs b/src/entity.rs index 3489671..f3ca328 100644 --- a/src/entity.rs +++ b/src/entity.rs @@ -8,7 +8,7 @@ pub trait Operable { fn draw(&self, spritebatch: &mut SpriteBatch); } -#[derive(Clone)] +#[derive(Debug, Clone)] pub struct Entity { pub position: Point2<f32>, pub spawn: Point2<f32>, diff --git a/src/game.rs b/src/game.rs new file mode 100644 index 0000000..2eabcb2 --- /dev/null +++ b/src/game.rs @@ -0,0 +1,81 @@ +use ggez::event::{EventHandler, KeyCode, KeyMods}; +use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image, WrapMode}; +use ggez::{Context, GameResult}; + +use crate::camera::Camera; +use crate::dialogbox::DialogBox; +use crate::entity::Operable; +use crate::world::World; + +pub struct Game { + world: World, + spritebatch: SpriteBatch, + dialogbox: DialogBox, + camera: Camera, +} + +impl Game { + pub fn new(context: &mut Context) -> GameResult<Game> { + let mut image = Image::new(context, "/tileset.png")?; + image.set_filter(FilterMode::Nearest); + image.set_wrap(WrapMode::Mirror, WrapMode::Mirror); + let world = World::new(context); + let dimensions = world.get_dimensions(); + + Ok(Game { + world, + spritebatch: SpriteBatch::new(image), + dialogbox: DialogBox::new(context), + camera: Camera::new(dimensions), + }) + } +} + +impl EventHandler for Game { + fn update(&mut self, _context: &mut Context) -> GameResult { + self.world.update(); + self.camera.give_center(self.world.player.get_position()); + self.dialogbox.update(); + Ok(()) + } + + fn draw(&mut self, context: &mut Context) -> GameResult { + graphics::clear(context, graphics::BLACK); + + self.world.draw(&mut self.spritebatch); + + graphics::draw( + context, + &self.spritebatch, + DrawParam::default().dest(self.camera.draw), + )?; + + self.dialogbox.draw(context)?; + + self.spritebatch.clear(); + + graphics::present(context)?; + + Ok(()) + } + + fn key_up_event(&mut self, _: &mut Context, keycode: KeyCode, _: KeyMods) { + self.world.give_key_up(keycode); + } + + fn key_down_event( + &mut self, + context: &mut Context, + keycode: KeyCode, + _: KeyMods, + repeat: bool, + ) { + if !repeat { + match keycode { + KeyCode::Q => context.continuing = false, + KeyCode::E => self.dialogbox.give_dialogtree(self.world.get_dialogtree()), + _ => self.world.give_key_down(keycode), + } + } + } +} @@ -4,6 +4,7 @@ pub mod cell; pub mod constants; pub mod dialogbox; pub mod entity; +pub mod game; pub mod layer; pub mod map; pub mod npc; diff --git a/src/main.rs b/src/main.rs index 78c60f9..e2465d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,7 @@ use ggez::conf::{NumSamples, WindowSetup}; use ggez::{event, ContextBuilder, GameResult}; -use pax_romana::world::World; +use pax_romana::game::Game; fn main() -> GameResult { let (ref mut context, ref mut event_loop) = ContextBuilder::new("pax-romana", "tom barrett") @@ -13,7 +13,7 @@ fn main() -> GameResult { .add_resource_path("./resources") .build()?; - let state = &mut World::new(context)?; + let game = &mut Game::new(context)?; - event::run(context, event_loop, state) + event::run(context, event_loop, game) } @@ -7,6 +7,7 @@ use xml::reader::XmlEvent::Characters; use crate::constants; use crate::entity::Operable; use crate::layer::Layer; +use crate::npc::Character; use crate::tile::Tile; use crate::tileset::Tileset; use crate::xmlelements::XMLElements; @@ -90,11 +91,11 @@ impl Map { spawn_points } - pub fn get_spawn_points(&self, name: &str) -> Vec<Point2<f32>> { + pub fn get_spawn_points(&self, character: Character) -> Vec<Point2<f32>> { self.spawns .clone() .into_iter() - .filter(|s| s.0 == name) + .filter(|s| s.0 == character.to_str()) .map(|s| s.1) .collect() } @@ -1,20 +1,39 @@ use ggez::graphics::spritebatch::SpriteBatch; use ggez::nalgebra::{distance, Point2}; +use ggez::Context; use rand::Rng; use std::f32::consts::PI; use std::time::Instant; use crate::animations::Animations; use crate::constants; +use crate::dialogbox::DialogTree; use crate::entity::{Action, Entity, Operable}; use crate::map::Map; use crate::tileset::Tileset; -#[derive(Clone)] +#[derive(Debug, Clone, Copy)] +pub enum Character { + Player, + Peasant, +} + +impl Character { + pub fn to_str(&self) -> &str { + match self { + Character::Player => "player", + Character::Peasant => "peasant", + } + } +} + +#[derive(Debug, Clone)] pub struct NPC { - entity: Entity, + pub entity: Entity, behavior: Behavior, animations: Animations, + dialogtree: DialogTree, + character: Character, } impl Operable for NPC { @@ -26,6 +45,7 @@ impl Operable for NPC { match self.behavior { Behavior::Wandering(destination) => self.move_torwards(destination), Behavior::Waiting(time) => self.wait(time), + Behavior::Talking => (), } self.entity.update(); self.animations.update(&self.entity.action); @@ -33,8 +53,16 @@ impl Operable for NPC { } impl NPC { - pub fn new(tileset: &Tileset, spawn: Point2<f32>, map_dimensions: (f32, f32)) -> NPC { + pub fn new( + character: Character, + context: &mut Context, + tileset: &Tileset, + spawn: Point2<f32>, + map_dimensions: (f32, f32), + ) -> NPC { NPC { + character, + dialogtree: DialogTree::new(context, character), entity: Entity::new(spawn, map_dimensions), behavior: Behavior::Wandering(random_nearby_point(spawn, constants::WANDER_DISTANCE)), animations: Animations::new(tileset), @@ -44,16 +72,16 @@ impl NPC { fn move_torwards(&mut self, destination: Point2<f32>) { let position = self.entity.position; - if distance(&position, &destination) < constants::GOAL_DISTANCE { + if distance(&position, &destination) < constants::INTERACT_DISTANCE { self.entity.action = Action::IdleRight; self.behavior = Behavior::Waiting(Instant::now()); - } else if (position.x - destination.x).abs() < constants::GOAL_DISTANCE { + } else if (position.x - destination.x).abs() < constants::INTERACT_DISTANCE { if position.y > destination.y { self.entity.action = Action::MovingUp; } else { self.entity.action = Action::MovingDown; } - } else if (position.y - destination.y).abs() < constants::GOAL_DISTANCE { + } else if (position.y - destination.y).abs() < constants::INTERACT_DISTANCE { if position.x > destination.x { self.entity.action = Action::MovingLeft; } else { @@ -83,19 +111,32 @@ impl NPC { } } - pub fn build_npcs(tileset: &Tileset, map: &Map) -> Vec<NPC> { + pub fn get_dialogtree(&mut self) -> DialogTree { + self.behavior = Behavior::Talking; + self.dialogtree.clone() + } + + pub fn build_npcs(context: &mut Context, tileset: &Tileset, map: &Map) -> Vec<NPC> { let mut npcs = Vec::new(); - for point in map.get_spawn_points("peasant") { - npcs.push(NPC::new(tileset, point, map.get_dimensions())); + let character = Character::Peasant; + for point in map.get_spawn_points(character) { + npcs.push(NPC::new( + character, + context, + tileset, + point, + map.get_dimensions(), + )); } npcs } } -#[derive(Clone)] +#[derive(Debug, Clone)] enum Behavior { + Talking, Waiting(Instant), Wandering(Point2<f32>), } diff --git a/src/player.rs b/src/player.rs index 025056c..7df0922 100644 --- a/src/player.rs +++ b/src/player.rs @@ -6,8 +6,9 @@ use crate::animations::Animations; use crate::entity::{Action, Entity, Operable}; use crate::tileset::Tileset; +#[derive(Clone)] pub struct Player { - entity: Entity, + pub entity: Entity, animations: Animations, } diff --git a/src/world.rs b/src/world.rs index abbb9c1..76048b9 100644 --- a/src/world.rs +++ b/src/world.rs @@ -1,102 +1,77 @@ -use ggez::event::{EventHandler, KeyCode, KeyMods}; -use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image, WrapMode}; -use ggez::{filesystem, Context, GameResult}; +use ggez::event::KeyCode; +use ggez::graphics::spritebatch::SpriteBatch; +use ggez::nalgebra::distance; +use ggez::{filesystem, Context}; -use crate::camera::Camera; -use crate::dialogbox::DialogBox; +use crate::constants; +use crate::dialogbox::DialogTree; use crate::entity::Operable; use crate::map::Map; -use crate::npc::NPC; +use crate::npc::{Character, NPC}; use crate::player::Player; use crate::tileset::Tileset; +#[derive(Clone)] pub struct World { map: Map, - spritebatch: SpriteBatch, - dialogbox: DialogBox, - camera: Camera, - player: Player, + pub player: Player, npcs: Vec<NPC>, } -impl World { - pub fn new(context: &mut Context) -> GameResult<World> { - let mut image = Image::new(context, "/tileset.png")?; - image.set_filter(FilterMode::Nearest); - image.set_wrap(WrapMode::Mirror, WrapMode::Mirror); +impl Operable for World { + fn update(&mut self) { + self.map.update(); + self.player.update(); + for npc in self.npcs.iter_mut() { + npc.update(); + } + } - let tileset = Tileset::new(filesystem::open(context, "/tileset.tsx")?); + fn draw(&self, spritebatch: &mut SpriteBatch) { + self.map.draw(spritebatch); + self.player.draw(spritebatch); + for npc in self.npcs.iter() { + npc.draw(spritebatch); + } + } +} - let map = Map::new(filesystem::open(context, "/map.tmx")?, &tileset); +impl World { + pub fn new(context: &mut Context) -> World { + let tileset = Tileset::new(filesystem::open(context, "/tileset.tsx").unwrap()); + let map = Map::new(filesystem::open(context, "/map.tmx").unwrap(), &tileset); - Ok(World { + World { map: map.clone(), - spritebatch: SpriteBatch::new(image), - dialogbox: DialogBox::new(context), - camera: Camera::new(map.get_dimensions()), player: Player::new( &tileset, - map.get_spawn_points("player")[0], + map.get_spawn_points(Character::Player)[0], map.get_dimensions(), ), - npcs: NPC::build_npcs(&tileset, &map), - }) - } -} - -impl EventHandler for World { - fn update(&mut self, _context: &mut Context) -> GameResult { - self.map.update(); - self.player.update(); - for npc in self.npcs.iter_mut() { - npc.update(); + npcs: NPC::build_npcs(context, &tileset, &map), } - - self.camera.give_center(self.player.get_position()); - Ok(()) } - fn draw(&mut self, context: &mut Context) -> GameResult { - graphics::clear(context, graphics::BLACK); - - self.map.draw(&mut self.spritebatch); - self.player.draw(&mut self.spritebatch); - for npc in self.npcs.iter_mut() { - npc.draw(&mut self.spritebatch); + pub fn get_dialogtree(&mut self) -> Option<DialogTree> { + let player_position = self.player.entity.position; + if let Some(npc) = self.npcs.iter_mut().find(|npc| { + constants::INTERACT_DISTANCE > distance(&player_position, &npc.entity.position) + }) { + Some(npc.get_dialogtree()) + } else { + None } + } - graphics::draw( - context, - &self.spritebatch, - DrawParam::default().dest(self.camera.draw), - )?; - - self.dialogbox.draw(context)?; - - self.spritebatch.clear(); - - graphics::present(context)?; - - Ok(()) + pub fn give_key_up(&mut self, keycode: KeyCode) { + self.player.give_key_up(keycode); } - fn key_up_event(&mut self, _: &mut Context, keycode: KeyCode, _: KeyMods) { - self.player.give_key_up(keycode) + pub fn give_key_down(&mut self, keycode: KeyCode) { + self.player.give_key_down(keycode); } - fn key_down_event( - &mut self, - context: &mut Context, - keycode: KeyCode, - _: KeyMods, - repeat: bool, - ) { - if !repeat { - match keycode { - KeyCode::Q => context.continuing = false, - KeyCode::E => self.dialogbox.visible = !self.dialogbox.visible, - _ => self.player.give_key_down(keycode), - } - } + pub fn get_dimensions(&self) -> (f32, f32) { + self.map.get_dimensions() } } |