summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/constants.rs9
-rw-r--r--src/layer.rs37
-rw-r--r--src/lib.rs3
-rw-r--r--src/map.rs91
-rw-r--r--src/math.rs5
-rw-r--r--src/player.rs6
-rw-r--r--src/state.rs12
-rw-r--r--src/tile.rs90
-rw-r--r--src/tileset.rs64
-rw-r--r--src/xmlelements.rs113
10 files changed, 337 insertions, 93 deletions
diff --git a/src/constants.rs b/src/constants.rs
index 367e595..c154066 100644
--- a/src/constants.rs
+++ b/src/constants.rs
@@ -4,8 +4,7 @@ pub const TILE_SCALE: f32 = 3.0;
pub const PLAYER_SPEED: f32 = 5.0;
-pub const FLIP_HORIZONTALLY_FLAG: u32 = 0x8000_0000;
-pub const FLIP_VERTICALLY_FLAG: u32 = 0x4000_0000;
-pub const FLIP_DIAGONALLY_FLAG: u32 = 0x2000_0000;
-pub const ALL_FLIP_FLAGS: u32 =
- FLIP_DIAGONALLY_FLAG | FLIP_HORIZONTALLY_FLAG | FLIP_VERTICALLY_FLAG;
+pub const FLIP_HORIZONTAL_FLAG: usize = 0x8000_0000;
+pub const FLIP_VERTICAL_FLAG: usize = 0x4000_0000;
+pub const FLIP_DIAGONAL_FLAG: usize = 0x2000_0000;
+pub const ALL_FLIP_FLAGS: usize = FLIP_DIAGONAL_FLAG | FLIP_HORIZONTAL_FLAG | FLIP_VERTICAL_FLAG;
diff --git a/src/layer.rs b/src/layer.rs
new file mode 100644
index 0000000..c9f32e8
--- /dev/null
+++ b/src/layer.rs
@@ -0,0 +1,37 @@
+use ggez::graphics::spritebatch::SpriteBatch;
+
+use crate::tile::Tile;
+use crate::tileset::Tileset;
+
+pub struct Layer {
+ pub tiles: Vec<Tile>,
+ pub width: usize,
+ pub height: usize,
+}
+
+impl Layer {
+ pub fn new(text: &str, tileset: &Tileset, width: usize, height: usize) -> Layer {
+ Layer {
+ tiles: text
+ .replace("\n", "")
+ .split(',')
+ .enumerate()
+ .map(|(i, s)| Tile::new(s, i, tileset, width, height))
+ .collect(),
+ width,
+ height,
+ }
+ }
+
+ pub fn update(&mut self) {
+ for tile in self.tiles.iter_mut() {
+ tile.update();
+ }
+ }
+
+ pub fn draw(&self, spritebatch: &mut SpriteBatch) {
+ for tile in self.tiles.iter() {
+ tile.draw(spritebatch);
+ }
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 583c6f7..ac7c4f3 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,7 +1,10 @@
pub mod camera;
pub mod constants;
+pub mod layer;
pub mod map;
+pub mod math;
pub mod player;
pub mod state;
+pub mod tile;
pub mod tileset;
pub mod xmlelements;
diff --git a/src/map.rs b/src/map.rs
index bb5f694..ab436d1 100644
--- a/src/map.rs
+++ b/src/map.rs
@@ -1,10 +1,9 @@
use ggez::filesystem::File;
-use ggez::graphics::{spritebatch::SpriteBatch, DrawParam};
-use ggez::nalgebra::{Point2, Vector2};
-use std::f32::consts::PI;
+use ggez::graphics::spritebatch::SpriteBatch;
use xml::reader::XmlEvent::Characters;
use crate::constants;
+use crate::layer::Layer;
use crate::tileset::Tileset;
use crate::xmlelements::XMLElements;
@@ -15,17 +14,26 @@ pub struct Map {
}
impl Map {
- pub fn new(file: File) -> Map {
+ pub fn new(file: File, tileset: &Tileset) -> Map {
let elements = XMLElements::new(file);
- let width = elements.get_element_attribute("map", "width").unwrap();
- let height = elements.get_element_attribute("map", "height").unwrap();
+ let width = elements
+ .get_element_attribute("map", "width")
+ .unwrap()
+ .parse()
+ .unwrap();
+ let height = elements
+ .get_element_attribute("map", "height")
+ .unwrap()
+ .parse()
+ .unwrap();
+
let layers = elements
.events
.iter()
.filter_map(|e| {
if let Characters(text) = e {
- Some(Layer::new(text.to_string()))
+ Some(Layer::new(text, tileset, width, height))
} else {
None
}
@@ -39,28 +47,15 @@ impl Map {
}
}
- pub fn draw(&self, spritebatch: &mut SpriteBatch, tileset: &Tileset) {
+ pub fn draw(&self, spritebatch: &mut SpriteBatch) {
for layer in self.layers.iter() {
- for x in 0..self.width {
- for y in 0..self.height {
- let tile_id = layer.data[x + (y * self.height)];
- let (tile_id, rotate) = self.decode(tile_id as u32);
-
- let offset = (constants::TILE_WIDTH / 2.0) * constants::TILE_SCALE;
-
- let draw_param = DrawParam::default()
- .src(tileset.tiles[tile_id as usize])
- .rotation(rotate)
- .offset(Point2::new(0.5, 0.5))
- .dest(Point2::new(
- (constants::TILE_WIDTH * constants::TILE_SCALE * x as f32) + offset,
- (constants::TILE_HEIGHT * constants::TILE_SCALE * y as f32) + offset,
- ))
- .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE));
+ layer.draw(spritebatch);
+ }
+ }
- spritebatch.add(draw_param);
- }
- }
+ pub fn update(&mut self) {
+ for layer in self.layers.iter_mut() {
+ layer.update();
}
}
@@ -70,46 +65,4 @@ impl Map {
(constants::TILE_HEIGHT * constants::TILE_SCALE) * self.height as f32,
)
}
-
- fn decode(&self, tile_id: u32) -> (u32, f32) {
- //let flip_d = (tile_id & constants::FLIP_DIAGONALLY_FLAG) == constants::FLIP_DIAGONALLY_FLAG;
- let flip_h =
- (tile_id & constants::FLIP_HORIZONTALLY_FLAG) == constants::FLIP_HORIZONTALLY_FLAG;
- let flip_v = (tile_id & constants::FLIP_VERTICALLY_FLAG) == constants::FLIP_VERTICALLY_FLAG;
-
- let new_tile_id = if flip_h | flip_v {
- tile_id & !constants::ALL_FLIP_FLAGS
- } else {
- tile_id
- };
-
- let rotate = match (flip_h, flip_v) {
- (true, false) => self.convert_angle_to_rad(90.0),
- (true, true) => self.convert_angle_to_rad(180.0),
- (false, true) => self.convert_angle_to_rad(270.0),
- (false, false) => 0.0,
- };
-
- (new_tile_id, rotate)
- }
-
- fn convert_angle_to_rad(&self, angle: f32) -> f32 {
- angle * (PI / 180.0)
- }
-}
-
-pub struct Layer {
- pub data: Vec<usize>,
-}
-
-impl Layer {
- pub fn new(text: String) -> Layer {
- Layer {
- data: text
- .replace("\n", "")
- .split(',')
- .map(|s| s.parse().unwrap())
- .collect(),
- }
- }
}
diff --git a/src/math.rs b/src/math.rs
new file mode 100644
index 0000000..c7ea7f6
--- /dev/null
+++ b/src/math.rs
@@ -0,0 +1,5 @@
+use std::f32::consts::PI;
+
+pub fn convert_angle_to_rad(angle: f32) -> f32 {
+ angle * (PI / 180.0)
+}
diff --git a/src/player.rs b/src/player.rs
index bc019b9..e9c3a75 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -11,8 +11,8 @@ pub struct Player {
pub position: Point2<f32>,
state: PlayerState,
tile: Rect,
- animation: Vec<(u32, Rect)>,
- animations: HashMap<PlayerState, Vec<(u32, Rect)>>,
+ animation: Vec<(usize, Rect)>,
+ animations: HashMap<PlayerState, Vec<(usize, Rect)>>,
map_height: f32,
map_width: f32,
}
@@ -106,7 +106,7 @@ impl Player {
let index = match self.animation.iter().position(|a| a.1 == self.tile) {
Some(index) => {
- if check_update_time(context, self.animation[index].0) {
+ if check_update_time(context, self.animation[index].0 as u32) {
index + 1
} else {
index
diff --git a/src/state.rs b/src/state.rs
index b0a497c..a981924 100644
--- a/src/state.rs
+++ b/src/state.rs
@@ -1,5 +1,5 @@
use ggez::event::{EventHandler, KeyCode, KeyMods};
-use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image};
+use ggez::graphics::{self, spritebatch::SpriteBatch, DrawParam, FilterMode, Image, WrapMode};
use ggez::{filesystem, Context, GameResult};
use crate::camera::Camera;
@@ -9,7 +9,6 @@ use crate::tileset::Tileset;
pub struct State {
map: Map,
- tileset: Tileset,
spritebatch: SpriteBatch,
camera: Camera,
player: Player,
@@ -19,13 +18,15 @@ impl State {
pub fn new(context: &mut Context) -> GameResult<State> {
let mut image = Image::new(context, "/tileset.png")?;
image.set_filter(FilterMode::Nearest);
+ image.set_wrap(WrapMode::Mirror, WrapMode::Mirror);
- let map = Map::new(filesystem::open(context, "/map.tmx")?);
+ let tileset = Tileset::new(filesystem::open(context, "/tileset.tsx")?);
+
+ let map = Map::new(filesystem::open(context, "/map.tmx")?, &tileset);
let map_dimensions = map.get_dimensions();
Ok(State {
map,
- tileset: Tileset::new(filesystem::open(context, "/tileset.tsx")?),
spritebatch: SpriteBatch::new(image),
camera: Camera::new(context, map_dimensions),
player: Player::new(map_dimensions),
@@ -35,6 +36,7 @@ impl State {
impl EventHandler for State {
fn update(&mut self, context: &mut Context) -> GameResult {
+ self.map.update();
self.player.update(context);
self.camera.give_center(self.player.position);
Ok(())
@@ -43,7 +45,7 @@ impl EventHandler for State {
fn draw(&mut self, context: &mut Context) -> GameResult {
graphics::clear(context, graphics::BLACK);
- self.map.draw(&mut self.spritebatch, &self.tileset);
+ self.map.draw(&mut self.spritebatch);
self.player.draw(&mut self.spritebatch);
graphics::draw(
diff --git a/src/tile.rs b/src/tile.rs
new file mode 100644
index 0000000..cb2aa83
--- /dev/null
+++ b/src/tile.rs
@@ -0,0 +1,90 @@
+use ggez::graphics::{spritebatch::SpriteBatch, DrawParam, Rect};
+use ggez::nalgebra::{Point2, Vector2};
+use std::time::Instant;
+
+use crate::constants;
+use crate::math::convert_angle_to_rad;
+use crate::tileset::Tileset;
+
+pub struct Tile {
+ source: Rect,
+ animations: Option<Vec<(usize, Rect)>>,
+ timer: Instant,
+ destination: Point2<f32>,
+ rotation: f32,
+}
+
+impl Tile {
+ pub fn new(text: &str, i: usize, tileset: &Tileset, width: usize, height: usize) -> Tile {
+ let id = text.parse::<usize>().unwrap();
+
+ let flip_d = (id & constants::FLIP_DIAGONAL_FLAG) == constants::FLIP_DIAGONAL_FLAG;
+ let flip_h = (id & constants::FLIP_HORIZONTAL_FLAG) == constants::FLIP_HORIZONTAL_FLAG;
+ let flip_v = (id & constants::FLIP_VERTICAL_FLAG) == constants::FLIP_VERTICAL_FLAG;
+
+ let id = if flip_h | flip_v | flip_d {
+ id & !constants::ALL_FLIP_FLAGS
+ } else {
+ id
+ };
+
+ let (source, rotation) = match (flip_d, flip_h, flip_v) {
+ (true, true, true) => (Tile::flip(tileset.get(id)), convert_angle_to_rad(90.0)),
+ (true, true, false) => (tileset.get(id), convert_angle_to_rad(90.0)),
+ (true, false, true) => (tileset.get(id), convert_angle_to_rad(270.0)),
+ //(true, false, false) => (),
+ (false, true, true) => (tileset.get(id), convert_angle_to_rad(180.0)),
+ (false, true, false) => (Tile::flip(tileset.get(id)), 0.0),
+ //(false, false, true) => (),
+ //(false, false, false) => (),
+ _ => (tileset.get(id), 0.0),
+ };
+
+ let x = i as f32 % width as f32;
+ let y = (i as f32 / height as f32).floor();
+ let offset = (constants::TILE_WIDTH / 2.0) * constants::TILE_SCALE;
+
+ let destination = Point2::new(
+ (constants::TILE_WIDTH * constants::TILE_SCALE * x) + offset,
+ (constants::TILE_HEIGHT * constants::TILE_SCALE * y) + offset,
+ );
+
+ Tile {
+ source,
+ animations: tileset.get_animations(id),
+ timer: Instant::now(),
+ destination,
+ rotation,
+ }
+ }
+
+ fn flip(rect: Rect) -> Rect {
+ let mut r = rect;
+ r.x *= -1.0;
+ r.x -= rect.w;
+ r
+ }
+
+ pub fn update(&mut self) {
+ if let Some(animations) = &self.animations {
+ if let Some(mut i) = animations.iter().position(|a| a.1 == self.source) {
+ if self.timer.elapsed().as_millis() > animations[i].0 as u128 {
+ i = if i == animations.len() - 1 { 0 } else { i + 1 };
+ self.source = animations[i].1;
+ self.timer = Instant::now()
+ }
+ }
+ }
+ }
+
+ pub fn draw(&self, spritebatch: &mut SpriteBatch) {
+ let draw_param = DrawParam::default()
+ .src(self.source)
+ .dest(self.destination)
+ .offset(Point2::new(0.5, 0.5))
+ .rotation(self.rotation)
+ .scale(Vector2::new(constants::TILE_SCALE, constants::TILE_SCALE));
+
+ spritebatch.add(draw_param);
+ }
+}
diff --git a/src/tileset.rs b/src/tileset.rs
index ae65097..548cde6 100644
--- a/src/tileset.rs
+++ b/src/tileset.rs
@@ -1,37 +1,87 @@
use ggez::filesystem::File;
use ggez::graphics::Rect;
+use std::collections::HashMap;
use crate::constants;
-use crate::xmlelements::XMLElements;
+use crate::xmlelements::{Property, XMLElements};
pub struct Tileset {
- pub tiles: Vec<Rect>,
+ tiles: HashMap<usize, Rect>,
+ properties: HashMap<usize, Property>,
}
impl Tileset {
pub fn new(file: File) -> Tileset {
let elements = XMLElements::new(file);
- let height = elements.get_element_attribute("image", "height").unwrap();
+ let height = elements
+ .get_element_attribute("image", "height")
+ .unwrap()
+ .parse::<usize>()
+ .unwrap();
let columns = elements
.get_element_attribute("tileset", "columns")
+ .unwrap()
+ .parse::<usize>()
.unwrap();
let rows = height / (constants::TILE_HEIGHT as usize);
- let mut tiles = Vec::new();
- tiles.push(Rect::zero());
+ let mut tiles = HashMap::new();
+ tiles.insert(0, Rect::zero());
let w = 1.0 / columns as f32;
let h = 1.0 / rows as f32;
+ let mut key = 1;
for r in 0..rows {
for c in 0..columns {
let x = c as f32 / columns as f32;
let y = r as f32 / rows as f32;
- tiles.push(Rect::new(x, y, w, h));
+ tiles.insert(key, Rect::new(x, y, w, h));
+ key += 1;
}
}
- Tileset { tiles }
+ let mut properties = HashMap::new();
+
+ for tile_element in elements.get_elements("tile") {
+ let tile_id = XMLElements::get_attribute(&tile_element, "id")
+ .unwrap()
+ .parse::<usize>()
+ .unwrap();
+
+ let property_elements = elements.get_children(&tile_element, "property");
+
+ properties.insert(tile_id + 1, Property::new(property_elements));
+ }
+
+ Tileset { tiles, properties }
+ }
+
+ pub fn get(&self, id: usize) -> Rect {
+ *self.tiles.get(&id).unwrap()
+ }
+
+ pub fn get_animations(&self, id: usize) -> Option<Vec<(usize, Rect)>> {
+ if let Some(property) = self.properties.get(&id) {
+ let entitys_properties: HashMap<usize, Property> = self
+ .properties
+ .clone()
+ .into_iter()
+ .filter(|(_, p)| p.entity == property.entity)
+ .collect();
+ Some(
+ entitys_properties
+ .iter()
+ .map(|(id, p)| (p.delay, self.get(*id)))
+ .collect(),
+ )
+ } else {
+ None
+ }
+ }
+
+ pub fn get_property(&self, id: usize) -> Option<&Property> {
+ self.properties.get(&id)
}
}
diff --git a/src/xmlelements.rs b/src/xmlelements.rs
index 86d7c77..ea880c8 100644
--- a/src/xmlelements.rs
+++ b/src/xmlelements.rs
@@ -2,9 +2,39 @@ use ggez::filesystem::File;
use std::io::BufReader;
use xml::reader::{
EventReader,
- XmlEvent::{self, StartElement},
+ XmlEvent::{self, EndElement, StartElement},
};
+#[derive(Debug, Clone)]
+pub struct Property {
+ pub entity: String,
+ pub keyframe: usize,
+ pub delay: usize,
+}
+
+impl Property {
+ pub fn new(property_elements: Vec<XmlEvent>) -> Property {
+ let entity = XMLElements::get_attribute_value(&property_elements, "entity")
+ .unwrap()
+ .parse()
+ .unwrap();
+ let keyframe = XMLElements::get_attribute_value(&property_elements, "keyframe")
+ .unwrap()
+ .parse()
+ .unwrap();
+ let delay = XMLElements::get_attribute_value(&property_elements, "delay")
+ .unwrap()
+ .parse()
+ .unwrap();
+
+ Property {
+ entity,
+ keyframe,
+ delay,
+ }
+ }
+}
+
pub struct XMLElements {
pub events: Vec<XmlEvent>,
}
@@ -33,20 +63,95 @@ impl XMLElements {
.unwrap()
}
+ pub fn get_children(&self, element: &XmlEvent, children_name: &str) -> Vec<XmlEvent> {
+ let start_index = self.events.iter().position(|e| e == element).unwrap();
+
+ let element_name = if let StartElement { name, .. } = element {
+ &name.local_name
+ } else {
+ ""
+ };
+
+ let end_index = self.events[start_index..]
+ .iter()
+ .position(|e| {
+ if let EndElement { name } = e {
+ element_name == name.local_name
+ } else {
+ false
+ }
+ })
+ .unwrap()
+ + start_index;
+
+ self.events[start_index..end_index]
+ .iter()
+ .cloned()
+ .filter(|e| {
+ if let StartElement { name, .. } = e {
+ name.local_name == children_name
+ } else {
+ false
+ }
+ })
+ .collect()
+ }
+
+ pub fn get_elements(&self, element_name: &str) -> Vec<XmlEvent> {
+ self.events
+ .clone()
+ .into_iter()
+ .filter(|e| {
+ if let StartElement { name, .. } = e {
+ name.local_name == element_name
+ } else {
+ false
+ }
+ })
+ .collect()
+ }
+
pub fn get_element_attribute(
&self,
element_name: &str,
attribute_name: &str,
- ) -> Result<usize, ()> {
+ ) -> Result<String, ()> {
let element = self.get_element(element_name);
+ XMLElements::get_attribute(&element, attribute_name)
+ }
+
+ pub fn get_attribute(element: &XmlEvent, attribute_name: &str) -> Result<String, ()> {
if let StartElement { attributes, .. } = element {
Ok(attributes
.iter()
.find(|a| a.name.local_name == attribute_name)
.unwrap()
.value
- .parse()
- .unwrap())
+ .clone())
+ } else {
+ Err(())
+ }
+ }
+
+ pub fn get_attribute_value(elements: &[XmlEvent], attribute_name: &str) -> Result<String, ()> {
+ let element = elements
+ .iter()
+ .find(|e| {
+ if let StartElement { attributes, .. } = e {
+ attributes.iter().any(|a| a.value == attribute_name)
+ } else {
+ false
+ }
+ })
+ .unwrap();
+
+ if let StartElement { attributes, .. } = element {
+ Ok(attributes
+ .iter()
+ .find(|a| a.name.local_name == "value")
+ .unwrap()
+ .value
+ .clone())
} else {
Err(())
}