Bringing back things together
This commit is contained in:
+22
-19
@@ -5,10 +5,10 @@ use sfml::system::Vector2;
|
||||
use sfml::window::Event;
|
||||
|
||||
use crate::engine::controls::{Action, Controls};
|
||||
use crate::engine::scene::Updatable;
|
||||
use crate::engine::math::{clamp, duration_as_f32, duration_as_frame};
|
||||
use crate::engine::physics;
|
||||
use crate::engine::renderer::Drawable;
|
||||
use crate::engine::scene::Updatable;
|
||||
use crate::engine::texture::Texture;
|
||||
|
||||
/// The different sides a character can face.
|
||||
@@ -50,8 +50,8 @@ impl Side {
|
||||
/// Returns the offset in the texture.
|
||||
pub fn offset(&self) -> i32 {
|
||||
match *self {
|
||||
Side::Left => 0,
|
||||
Side::Right => 32,
|
||||
Side::Left => 32,
|
||||
Side::Right => 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,10 @@ pub struct Character {
|
||||
max_jump: usize,
|
||||
|
||||
/// The timer of the character's animation.
|
||||
animation_timer: Option<Instant>,
|
||||
animation_timer: Instant,
|
||||
|
||||
/// Whether the character is walking or not.
|
||||
walking: bool,
|
||||
|
||||
/// Indicates that the player has released the jump button.
|
||||
can_jump: bool,
|
||||
@@ -101,8 +104,9 @@ impl Character {
|
||||
hp: 1,
|
||||
jump_counter: 1,
|
||||
max_jump: 1,
|
||||
animation_timer: None,
|
||||
animation_timer: Instant::now(),
|
||||
can_jump: true,
|
||||
walking: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +157,7 @@ impl Character {
|
||||
|
||||
/// Returns the collision bounding box of the character.
|
||||
pub fn bbox(&self) -> FloatRect {
|
||||
FloatRect::new(self.position.x + 8.0, self.position.y + 16.0, 16.0, 16.0)
|
||||
FloatRect::new(self.position.x + 8.0, self.position.y, 16.0, 32.0)
|
||||
}
|
||||
|
||||
/// Returns the number of hp of the character.
|
||||
@@ -190,13 +194,16 @@ impl Updatable for Character {
|
||||
}
|
||||
|
||||
if let Some(side) = Side::from_force(force) {
|
||||
self.side = side;
|
||||
|
||||
if self.animation_timer.is_none() {
|
||||
self.animation_timer = Some(Instant::now());
|
||||
if !self.walking {
|
||||
self.animation_timer = Instant::now();
|
||||
}
|
||||
self.walking = true;
|
||||
self.side = side;
|
||||
} else {
|
||||
self.animation_timer = None;
|
||||
if self.walking {
|
||||
self.animation_timer = Instant::now();
|
||||
}
|
||||
self.walking = false;
|
||||
}
|
||||
|
||||
let duration = duration_as_f32(duration);
|
||||
@@ -247,17 +254,13 @@ impl Updatable for Character {
|
||||
|
||||
impl Drawable for Character {
|
||||
fn texture(&self) -> Texture {
|
||||
Texture::Mario
|
||||
Texture::Rusty
|
||||
}
|
||||
|
||||
fn texture_rect(&self) -> IntRect {
|
||||
let frame = if let Some(started) = self.animation_timer {
|
||||
1 - duration_as_frame(&Instant::now().duration_since(started), 2)
|
||||
} else {
|
||||
0
|
||||
};
|
||||
|
||||
IntRect::new(self.side.offset(), frame * 32, 32, 32)
|
||||
let frame = duration_as_frame(&Instant::now().duration_since(self.animation_timer), 4);
|
||||
let offset = if self.walking { 64 } else { 0 };
|
||||
IntRect::new(self.side.offset() + offset, frame * 32, 32, 32)
|
||||
}
|
||||
|
||||
fn position(&self) -> Vector2<f32> {
|
||||
|
||||
@@ -67,7 +67,6 @@ make_fonts!(
|
||||
impl FontManager {
|
||||
/// Creates a fonts from an array of bytes.
|
||||
fn _make_font_from_bytes(bytes: Vec<u8>) -> SfFont {
|
||||
SfFont::from_memory(&bytes)
|
||||
.expect("Failed to create font")
|
||||
SfFont::from_memory(&bytes).expect("Failed to create font")
|
||||
}
|
||||
}
|
||||
|
||||
+105
-80
@@ -1,19 +1,15 @@
|
||||
use std::path::Path;
|
||||
use std::fs::File;
|
||||
use std::io::{BufWriter, BufReader};
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
||||
use bincode::{serialize, deserialize, serialize_into, deserialize_from};
|
||||
use std::io::Read;
|
||||
use std::path::Path;
|
||||
|
||||
use sfml::graphics::{FloatRect, IntRect};
|
||||
use sfml::system::Vector2;
|
||||
|
||||
use crate::{Error, Result};
|
||||
use crate::engine::character::Damage;
|
||||
use crate::engine::math::{clamp, Matrix};
|
||||
use crate::engine::renderer::Drawable;
|
||||
use crate::engine::texture::{Texture, byte_to_index, SPRITE_SIZE_F32, SPRITE_SIZE_I32};
|
||||
use crate::engine::character::Damage;
|
||||
use crate::engine::texture::{byte_to_index, Texture, SPRITE_SIZE_F32, SPRITE_SIZE_I32};
|
||||
use crate::{Error, Result};
|
||||
|
||||
/// This enum represents if the collision happens on the X axis or the Y axis.
|
||||
#[derive(Copy, Clone)]
|
||||
@@ -47,7 +43,7 @@ impl CollisionAxis {
|
||||
}
|
||||
|
||||
/// This struct represents the different sides from which a collision can occur.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct CollisionTile {
|
||||
/// If the character comes from the top, it will collide if this bool is true.
|
||||
pub from_top: bool,
|
||||
@@ -95,12 +91,10 @@ impl CollisionTile {
|
||||
}
|
||||
|
||||
/// This struct represents a renderable tile linking to its part in the tileset texture.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub struct GraphicTile(Option<i32>);
|
||||
|
||||
impl GraphicTile {
|
||||
|
||||
|
||||
/// Creates the correct graphic tile depending on the neighbours.
|
||||
///
|
||||
/// A none will be considered solid.
|
||||
@@ -119,7 +113,6 @@ impl GraphicTile {
|
||||
|
||||
/// Creates the correct graphic tile depending on the neighbours.
|
||||
pub fn from_neighbours(tiles: &[CollisionTile; 8]) -> GraphicTile {
|
||||
|
||||
let mut byte = 0;
|
||||
|
||||
if !tiles[7].is_full() || !tiles[0].is_full() || !tiles[1].is_full() {
|
||||
@@ -130,11 +123,11 @@ impl GraphicTile {
|
||||
byte += 2;
|
||||
}
|
||||
|
||||
if !tiles[1] .is_full() || !tiles[2].is_full() || !tiles[3].is_full() {
|
||||
if !tiles[1].is_full() || !tiles[2].is_full() || !tiles[3].is_full() {
|
||||
byte += 4;
|
||||
}
|
||||
|
||||
if !tiles[3] .is_full() {
|
||||
if !tiles[3].is_full() {
|
||||
byte += 8;
|
||||
}
|
||||
|
||||
@@ -150,7 +143,7 @@ impl GraphicTile {
|
||||
byte += 64;
|
||||
}
|
||||
|
||||
if !tiles[7] .is_full() {
|
||||
if !tiles[7].is_full() {
|
||||
byte += 128;
|
||||
}
|
||||
|
||||
@@ -161,7 +154,7 @@ impl GraphicTile {
|
||||
pub fn offset(self) -> (i32, i32) {
|
||||
match self.0 {
|
||||
None => (0, 0),
|
||||
Some(v) => (32 * v, 0)
|
||||
Some(v) => (32 * v, 0),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -198,23 +191,6 @@ impl Drawable for PositionedTile {
|
||||
}
|
||||
}
|
||||
|
||||
/// The content of a map that needs to be saved.
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SaveMap {
|
||||
/// The entrance point of the character in the map.
|
||||
entrance: (usize, usize),
|
||||
|
||||
/// The collision tiles contained in the level.
|
||||
collision_tiles: Matrix<CollisionTile>,
|
||||
}
|
||||
|
||||
impl SaveMap {
|
||||
/// Creates a map from the save map.
|
||||
fn to_map(self) -> Map {
|
||||
Map::from_entrance_and_collision_tiles(self.entrance, self.collision_tiles)
|
||||
}
|
||||
}
|
||||
|
||||
/// The map represents the tiles contained in a level.
|
||||
#[derive(Clone)]
|
||||
pub struct Map {
|
||||
@@ -241,39 +217,69 @@ impl Map {
|
||||
Map::from_collision_tiles(tiles)
|
||||
}
|
||||
|
||||
/// Creates a SaveMap from a map
|
||||
fn save_map(&self) -> SaveMap {
|
||||
SaveMap {
|
||||
entrance: self.entrance,
|
||||
collision_tiles: self.collision_tiles.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes the map into a binary format.
|
||||
pub fn encode(&self) -> Result<Vec<u8>> {
|
||||
serialize(&self.save_map()).map_err(Error::Encoding)
|
||||
}
|
||||
|
||||
/// Decodes a map from bytes.
|
||||
pub fn decode(content: &[u8]) -> Result<Map> {
|
||||
deserialize(content).map(SaveMap::to_map).map_err(Error::Decoding)
|
||||
}
|
||||
|
||||
/// Saves the map to a file.
|
||||
pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
let file = File::create(path.as_ref()).map_err(Error::Save)?;
|
||||
let mut writer = BufWriter::new(file);
|
||||
serialize_into(&mut writer, &self.save_map()).map_err(Error::Encoding)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Loads a map from a file.
|
||||
pub fn load<P: AsRef<Path>>(path: P) -> Result<Map> {
|
||||
let file = File::open(path.as_ref()).map_err(Error::Load)?;
|
||||
let mut reader = BufReader::new(file);
|
||||
Ok(deserialize_from(&mut reader).map(SaveMap::to_map).map_err(Error::Decoding)?)
|
||||
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Map> {
|
||||
let mut file = File::open(path.as_ref()).map_err(Error::Load)?;
|
||||
let mut s = String::new();
|
||||
file.read_to_string(&mut s).map_err(Error::Load)?;
|
||||
Map::from_str(&s)
|
||||
}
|
||||
|
||||
/// Loads a map from a string.
|
||||
pub fn from_str(text: &str) -> Result<Map> {
|
||||
let split = text.split('\n').collect::<Vec<_>>();
|
||||
|
||||
// First two usize are the size of the map
|
||||
let size = split[0]
|
||||
.split_whitespace()
|
||||
.map(|x| x.parse::<usize>().unwrap())
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
let mut tiles = Matrix::from_size(size[0], size[1], CollisionTile::empty());
|
||||
|
||||
for (row, line) in split.iter().skip(1).enumerate() {
|
||||
for (col, tile) in line.split_whitespace().enumerate() {
|
||||
let num = tile.parse::<u8>().unwrap();
|
||||
match num {
|
||||
0 => (),
|
||||
1 => tiles[(row, col)] = CollisionTile::full(),
|
||||
_ => panic!("Expecting 0 or 1 in level files"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(Map::from_collision_tiles(tiles))
|
||||
}
|
||||
|
||||
// /// Encodes the map into a binary format.
|
||||
// pub fn encode(&self) -> Result<Vec<u8>> {
|
||||
// serialize(&self.save_map()).map_err(Error::Encoding)
|
||||
// }
|
||||
|
||||
// /// Decodes a map from bytes.
|
||||
// pub fn decode(content: &[u8]) -> Result<Map> {
|
||||
// deserialize(content)
|
||||
// .map(SaveMap::to_map)
|
||||
// .map_err(Error::Decoding)
|
||||
// }
|
||||
|
||||
// /// Saves the map to a file.
|
||||
// pub fn save<P: AsRef<Path>>(&self, path: P) -> Result<()> {
|
||||
// let file = File::create(path.as_ref()).map_err(Error::Save)?;
|
||||
// let mut writer = BufWriter::new(file);
|
||||
// serialize_into(&mut writer, &self.save_map()).map_err(Error::Encoding)?;
|
||||
// Ok(())
|
||||
// }
|
||||
|
||||
// /// Loads a map from a file.
|
||||
// pub fn load<P: AsRef<Path>>(path: P) -> Result<Map> {
|
||||
// let file = File::open(path.as_ref()).map_err(Error::Load)?;
|
||||
// let mut reader = BufReader::new(file);
|
||||
// Ok(deserialize_from(&mut reader)
|
||||
// .map(SaveMap::to_map)
|
||||
// .map_err(Error::Decoding)?)
|
||||
// }
|
||||
|
||||
/// Creates a map from its entrance and collision tiles.
|
||||
pub fn from_entrance_and_collision_tiles(e: (usize, usize), t: Matrix<CollisionTile>) -> Map {
|
||||
let rows = t.rows();
|
||||
@@ -287,8 +293,8 @@ impl Map {
|
||||
graphic_tiles,
|
||||
};
|
||||
|
||||
for i in 0 .. rows {
|
||||
for j in 0 .. cols {
|
||||
for i in 0..rows {
|
||||
for j in 0..cols {
|
||||
map.graphic_tiles[(i, j)] = map.graphic_tile(i, j);
|
||||
}
|
||||
}
|
||||
@@ -305,14 +311,34 @@ impl Map {
|
||||
/// Creates the neighbours of a tile.
|
||||
pub fn neighbours(&self, i: usize, j: usize) -> [Option<CollisionTile>; 8] {
|
||||
[
|
||||
if i > 0 && j > 0 { self.collision_tiles.get(i - 1, j - 1).cloned() } else { None },
|
||||
if i > 0 { self.collision_tiles.get(i - 1, j ).cloned() } else { None },
|
||||
if i > 0 { self.collision_tiles.get(i - 1, j + 1).cloned() } else { None },
|
||||
self.collision_tiles.get(i , j + 1).cloned(),
|
||||
self.collision_tiles.get(i + 1, j + 1).cloned(),
|
||||
self.collision_tiles.get(i + 1, j ).cloned(),
|
||||
if j > 0 { self.collision_tiles.get(i + 1, j - 1).cloned() } else { None },
|
||||
if j > 0 { self.collision_tiles.get(i , j - 1).cloned() } else { None },
|
||||
if i > 0 && j > 0 {
|
||||
self.collision_tiles.get(i - 1, j - 1).cloned()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if i > 0 {
|
||||
self.collision_tiles.get(i - 1, j).cloned()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if i > 0 {
|
||||
self.collision_tiles.get(i - 1, j + 1).cloned()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
self.collision_tiles.get(i, j + 1).cloned(),
|
||||
self.collision_tiles.get(i + 1, j + 1).cloned(),
|
||||
self.collision_tiles.get(i + 1, j).cloned(),
|
||||
if j > 0 {
|
||||
self.collision_tiles.get(i + 1, j - 1).cloned()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
if j > 0 {
|
||||
self.collision_tiles.get(i, j - 1).cloned()
|
||||
} else {
|
||||
None
|
||||
},
|
||||
]
|
||||
}
|
||||
|
||||
@@ -337,8 +363,8 @@ impl Map {
|
||||
|
||||
// Refresh the current graphic tile and their neighbours
|
||||
use std::cmp::max;
|
||||
for i in max(i, 1) - 1 ..= (i + 1) {
|
||||
for j in max(j, 1) - 1 ..= (j + 1) {
|
||||
for i in max(i, 1) - 1..=(i + 1) {
|
||||
for j in max(j, 1) - 1..=(j + 1) {
|
||||
let new_tile = self.graphic_tile(i, j);
|
||||
if let Some(tile) = self.graphic_tiles.get_mut(i, j) {
|
||||
*tile = new_tile;
|
||||
@@ -384,7 +410,6 @@ impl Map {
|
||||
old: FloatRect,
|
||||
new: FloatRect,
|
||||
) -> Option<(CollisionAxis, Vector2<f32>, Damage)> {
|
||||
|
||||
let mut damage = Damage::None;
|
||||
|
||||
let cols = self.collision_tiles.cols() - 1;
|
||||
@@ -400,8 +425,8 @@ impl Map {
|
||||
let mut collision_y = false;
|
||||
let mut new = new;
|
||||
|
||||
for col in min_col ..= max_col {
|
||||
for row in min_row ..= max_row {
|
||||
for col in min_col..=max_col {
|
||||
for row in min_row..=max_row {
|
||||
let tile_left = col as f32 * SPRITE_SIZE_F32;
|
||||
let tile_top = row as f32 * SPRITE_SIZE_F32;
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
use std::time::Duration;
|
||||
use std::ops::{Index, IndexMut};
|
||||
|
||||
use serde::{Serialize, Deserialize};
|
||||
use std::time::Duration;
|
||||
|
||||
/// Clamp a number between two boundaries.
|
||||
pub fn clamp(number: f32, min: f32, max: f32) -> f32 {
|
||||
@@ -18,7 +16,7 @@ pub fn clamp(number: f32, min: f32, max: f32) -> f32 {
|
||||
pub fn duration_as_frame(duration: &Duration, total: usize) -> i32 {
|
||||
let secs = duration_as_f32(duration);
|
||||
|
||||
(secs * 4.0) as i32 % total as i32
|
||||
(secs * 10.0) as i32 % total as i32
|
||||
}
|
||||
|
||||
/// Converts a duration into its number of seconds.
|
||||
@@ -27,7 +25,7 @@ pub fn duration_as_f32(duration: &Duration) -> f32 {
|
||||
}
|
||||
|
||||
/// A generic matrix type, useful for levels.
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
#[derive(Clone)]
|
||||
pub struct Matrix<T> {
|
||||
/// The number of rows of the matrix.
|
||||
rows: usize,
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
use std::time::Instant;
|
||||
|
||||
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, View, Text};
|
||||
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, Text, View};
|
||||
|
||||
use sfml::window::{Event, Style};
|
||||
|
||||
use sfml::system::Vector2;
|
||||
|
||||
use crate::engine::font::{Font, FontManager};
|
||||
use crate::engine::scene::Scene;
|
||||
use crate::engine::texture::{Texture, TextureManager};
|
||||
use crate::engine::font::{Font, FontManager};
|
||||
|
||||
/// Our custom drawable trait.
|
||||
pub trait Drawable {
|
||||
@@ -80,7 +80,11 @@ impl Renderer {
|
||||
}
|
||||
|
||||
/// Draws a drawable with a certain translation.
|
||||
pub fn translate_and_draw<D: Drawable, V: Into<Vector2<f32>>>(&mut self, drawable: &D, translation: V) {
|
||||
pub fn translate_and_draw<D: Drawable, V: Into<Vector2<f32>>>(
|
||||
&mut self,
|
||||
drawable: &D,
|
||||
translation: V,
|
||||
) {
|
||||
let texture = self.texture_manager.get(drawable.texture());
|
||||
let mut sprite = Sprite::with_texture(&texture);
|
||||
sprite.set_texture_rect(&drawable.texture_rect());
|
||||
|
||||
@@ -4,9 +4,9 @@ use sfml::graphics::View;
|
||||
use sfml::system::Vector2;
|
||||
use sfml::window::Event;
|
||||
|
||||
use crate::engine::texture::SPRITE_SIZE_F32;
|
||||
use crate::engine::character::Character;
|
||||
use crate::engine::map::Map;
|
||||
use crate::engine::texture::SPRITE_SIZE_F32;
|
||||
|
||||
/// Contains everything needed to play.
|
||||
pub struct Scene {
|
||||
@@ -86,14 +86,11 @@ impl Scene {
|
||||
|
||||
/// Updates the whole scene.
|
||||
pub fn update(&mut self, duration: &Duration) -> State {
|
||||
|
||||
let mut state = State::Finished;
|
||||
|
||||
for c in &mut self.characters {
|
||||
|
||||
// Don't need to update if the character is dead
|
||||
if c.is_alive() {
|
||||
|
||||
let old = c.bbox();
|
||||
|
||||
// Compute the offset between position and bbox
|
||||
@@ -112,7 +109,6 @@ impl Scene {
|
||||
}
|
||||
|
||||
c.take_damage(damage);
|
||||
|
||||
} else {
|
||||
c.fall_off();
|
||||
}
|
||||
@@ -121,7 +117,6 @@ impl Scene {
|
||||
if c.controls().is_some() {
|
||||
state = State::Running;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+30
-30
@@ -10,32 +10,32 @@ pub const SPRITE_SIZE_F32: f32 = 32.0;
|
||||
/// tileset.
|
||||
pub fn byte_to_index(byte: u8) -> i32 {
|
||||
match byte {
|
||||
0 => 0,
|
||||
1 => 1,
|
||||
4 => 2,
|
||||
5 => 3,
|
||||
7 => 4,
|
||||
16 => 5,
|
||||
17 => 6,
|
||||
20 => 7,
|
||||
21 => 8,
|
||||
23 => 9,
|
||||
28 => 10,
|
||||
29 => 11,
|
||||
31 => 12,
|
||||
64 => 13,
|
||||
65 => 14,
|
||||
68 => 15,
|
||||
69 => 16,
|
||||
71 => 17,
|
||||
80 => 18,
|
||||
81 => 19,
|
||||
84 => 20,
|
||||
85 => 21,
|
||||
87 => 22,
|
||||
92 => 23,
|
||||
93 => 24,
|
||||
95 => 25,
|
||||
0 => 0,
|
||||
1 => 1,
|
||||
4 => 2,
|
||||
5 => 3,
|
||||
7 => 4,
|
||||
16 => 5,
|
||||
17 => 6,
|
||||
20 => 7,
|
||||
21 => 8,
|
||||
23 => 9,
|
||||
28 => 10,
|
||||
29 => 11,
|
||||
31 => 12,
|
||||
64 => 13,
|
||||
65 => 14,
|
||||
68 => 15,
|
||||
69 => 16,
|
||||
71 => 17,
|
||||
80 => 18,
|
||||
81 => 19,
|
||||
84 => 20,
|
||||
85 => 21,
|
||||
87 => 22,
|
||||
92 => 23,
|
||||
93 => 24,
|
||||
95 => 25,
|
||||
112 => 26,
|
||||
113 => 27,
|
||||
116 => 28,
|
||||
@@ -120,10 +120,10 @@ macro_rules! make_textures {
|
||||
}
|
||||
|
||||
make_textures!(
|
||||
Mario,
|
||||
mario,
|
||||
make_mario_texture,
|
||||
"../../../assets/textures/mario.png",
|
||||
Rusty,
|
||||
rusty,
|
||||
make_rusty_texture,
|
||||
"../../../assets/textures/rusty.png",
|
||||
256,
|
||||
256,
|
||||
Overworld,
|
||||
|
||||
Reference in New Issue
Block a user