summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/constants.rs2
-rw-r--r--src/dialogbox.rs66
-rw-r--r--src/entity.rs2
-rw-r--r--src/game.rs81
-rw-r--r--src/lib.rs1
-rw-r--r--src/main.rs6
-rw-r--r--src/map.rs5
-rw-r--r--src/npc.rs61
-rw-r--r--src/player.rs3
-rw-r--r--src/world.rs119
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),
+ }
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index be462c9..af27ba7 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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)
}
diff --git a/src/map.rs b/src/map.rs
index 124125a..9bb3b47 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -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()
}
diff --git a/src/npc.rs b/src/npc.rs
index 92d7a1d..29400a0 100644
--- a/src/npc.rs
+++ b/src/npc.rs
@@ -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()
}
}