Compare commits
No commits in common. "master" and "collisions" have entirely different histories.
master
...
collisions
|
@ -44,6 +44,16 @@ name = "autocfg"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bincode"
|
||||||
|
version = "1.1.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bitflags"
|
name = "bitflags"
|
||||||
version = "1.0.4"
|
version = "1.0.4"
|
||||||
|
@ -544,9 +554,11 @@ dependencies = [
|
||||||
name = "rusty"
|
name = "rusty"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"image 0.21.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"imageproc 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"imageproc 0.18.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"sfml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"sfml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -565,6 +577,24 @@ name = "scopeguard"
|
||||||
version = "0.3.3"
|
version = "0.3.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde"
|
||||||
|
version = "1.0.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "serde_derive"
|
||||||
|
version = "1.0.89"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"quote 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"syn 0.15.29 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "sfml"
|
name = "sfml"
|
||||||
version = "0.14.0"
|
version = "0.14.0"
|
||||||
|
@ -675,6 +705,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
|
"checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71"
|
||||||
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
"checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652"
|
||||||
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
|
"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799"
|
||||||
|
"checksum bincode 1.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "3efe0b4c8eaeed8600549c29f538a6a11bf422858d0ed435b1d70ec4ab101190"
|
||||||
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
"checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12"
|
||||||
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
|
"checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb"
|
||||||
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
|
"checksum cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11d43355396e872eefb45ce6342e4374ed7bc2b3a502d1b28e36d6e23c05d1f4"
|
||||||
|
@ -736,6 +767,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
"checksum safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9604873ffe1980bc1f179103704a65c8aca141c248d9e52b7af95ff10578166e"
|
"checksum safe-transmute 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9604873ffe1980bc1f179103704a65c8aca141c248d9e52b7af95ff10578166e"
|
||||||
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
"checksum scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)" = "1d51f5df5af43ab3f1360b429fa5e0152ac5ce8c0bd6485cae490332e96846a8"
|
||||||
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
"checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27"
|
||||||
|
"checksum serde 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "92514fb95f900c9b5126e32d020f5c6d40564c27a5ea6d1d7d9f157a96623560"
|
||||||
|
"checksum serde_derive 1.0.89 (registry+https://github.com/rust-lang/crates.io-index)" = "bb6eabf4b5914e88e24eea240bb7c9f9a2cbc1bbbe8d961d381975ec3c6b806c"
|
||||||
"checksum sfml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47b06f9db4d3f8b936d232d92bfbd6335060ae51ddcf1be2e79361aeb5c8fef1"
|
"checksum sfml 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)" = "47b06f9db4d3f8b936d232d92bfbd6335060ae51ddcf1be2e79361aeb5c8fef1"
|
||||||
"checksum sfml-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff35ae173aff95bc967d1b4f0e131a8aec384c317fd057738da4b0f71c8cf841"
|
"checksum sfml-build 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff35ae173aff95bc967d1b4f0e131a8aec384c317fd057738da4b0f71c8cf841"
|
||||||
"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba"
|
"checksum stb_truetype 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "69b7df505db8e81d54ff8be4693421e5b543e08214bd8d99eb761fcb4d5668ba"
|
||||||
|
|
|
@ -7,6 +7,8 @@ edition = "2018"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
clap = "2.32.0"
|
clap = "2.32.0"
|
||||||
sfml = "0.14.0"
|
sfml = "0.14.0"
|
||||||
|
serde = { version = "1.0.89", features = ["derive"] }
|
||||||
|
bincode = "1.1.2"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
image = { version = "0.21.0" }
|
image = { version = "0.21.0" }
|
||||||
|
@ -16,3 +18,6 @@ imageproc = "0.18.0"
|
||||||
name = "rusty-maker"
|
name = "rusty-maker"
|
||||||
path = "src/app/game.rs"
|
path = "src/app/game.rs"
|
||||||
|
|
||||||
|
[[bin]]
|
||||||
|
name = "rusty-editor"
|
||||||
|
path = "src/app/editor/main.rs"
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 1008 B |
Binary file not shown.
Before Width: | Height: | Size: 2.0 KiB |
35
build.rs
35
build.rs
|
@ -1,7 +1,7 @@
|
||||||
use std::fs::create_dir_all;
|
use std::fs::create_dir_all;
|
||||||
|
|
||||||
use image::imageops::{rotate180, rotate270, rotate90};
|
use image::{RgbaImage, GenericImage};
|
||||||
use image::{GenericImage, RgbaImage};
|
use image::imageops::{rotate90, rotate180, rotate270};
|
||||||
|
|
||||||
fn superpose(image: &RgbaImage, superposition: &RgbaImage) -> RgbaImage {
|
fn superpose(image: &RgbaImage, superposition: &RgbaImage) -> RgbaImage {
|
||||||
let mut clone = image.clone();
|
let mut clone = image.clone();
|
||||||
|
@ -68,13 +68,7 @@ fn main() {
|
||||||
let image_29 = superpose(&image_1, &image_28);
|
let image_29 = superpose(&image_1, &image_28);
|
||||||
vec.push(&image_29);
|
vec.push(&image_29);
|
||||||
|
|
||||||
let image_31 = superpose(
|
let image_31 = superpose(&superpose(&border, &superpose(&rotate90(&border), &rotate90(&corner_inside))), &corner_horiz);
|
||||||
&superpose(
|
|
||||||
&border,
|
|
||||||
&superpose(&rotate90(&border), &rotate90(&corner_inside)),
|
|
||||||
),
|
|
||||||
&corner_horiz,
|
|
||||||
);
|
|
||||||
let image_31 = superpose(&image_31, &rotate180(&corner_vert));
|
let image_31 = superpose(&image_31, &rotate180(&corner_vert));
|
||||||
vec.push(&image_31);
|
vec.push(&image_31);
|
||||||
|
|
||||||
|
@ -138,17 +132,8 @@ fn main() {
|
||||||
let image_125 = superpose(&image_1, &image_124);
|
let image_125 = superpose(&image_1, &image_124);
|
||||||
vec.push(&image_125);
|
vec.push(&image_125);
|
||||||
|
|
||||||
let image_127 = superpose(
|
let image_127 = superpose(&superpose(&border, &superpose(&rotate90(&border), &rotate90(&corner_inside))), &corner_horiz);
|
||||||
&superpose(
|
let image_127 = superpose(&superpose(&image_127, &rotate180(&corner_inside)), &rotate180(&border));
|
||||||
&border,
|
|
||||||
&superpose(&rotate90(&border), &rotate90(&corner_inside)),
|
|
||||||
),
|
|
||||||
&corner_horiz,
|
|
||||||
);
|
|
||||||
let image_127 = superpose(
|
|
||||||
&superpose(&image_127, &rotate180(&corner_inside)),
|
|
||||||
&rotate180(&border),
|
|
||||||
);
|
|
||||||
let image_127 = superpose(&image_127, &rotate270(&corner_vert));
|
let image_127 = superpose(&image_127, &rotate270(&corner_vert));
|
||||||
vec.push(&image_127);
|
vec.push(&image_127);
|
||||||
|
|
||||||
|
@ -188,13 +173,7 @@ fn main() {
|
||||||
let image_253 = rotate180(&image_223);
|
let image_253 = rotate180(&image_223);
|
||||||
vec.push(&image_253);
|
vec.push(&image_253);
|
||||||
|
|
||||||
let image_255 = superpose(
|
let image_255 = superpose(&border, &superpose(&rotate90(&border), &superpose(&rotate180(&border), &rotate270(&border))));
|
||||||
&border,
|
|
||||||
&superpose(
|
|
||||||
&rotate90(&border),
|
|
||||||
&superpose(&rotate180(&border), &rotate270(&border)),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let image_255 = superpose(&image_255, &corner_inside);
|
let image_255 = superpose(&image_255, &corner_inside);
|
||||||
let image_255 = superpose(&image_255, &rotate90(&corner_inside));
|
let image_255 = superpose(&image_255, &rotate90(&corner_inside));
|
||||||
let image_255 = superpose(&image_255, &rotate180(&corner_inside));
|
let image_255 = superpose(&image_255, &rotate180(&corner_inside));
|
||||||
|
@ -218,3 +197,5 @@ fn main() {
|
||||||
|
|
||||||
output.save("assets/textures-generated/grass.png").unwrap();
|
output.save("assets/textures-generated/grass.png").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
use clap::{App, crate_version};
|
||||||
|
|
||||||
|
use rusty::app::editor::Editor;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let _ = App::new("Rusty Editor")
|
||||||
|
.version(crate_version!())
|
||||||
|
.get_matches();
|
||||||
|
|
||||||
|
Editor::new().run();
|
||||||
|
}
|
|
@ -0,0 +1,230 @@
|
||||||
|
use sfml::system::Vector2;
|
||||||
|
use sfml::window::{Event, Key, mouse::Button as MouseButton};
|
||||||
|
use sfml::graphics::{Color, RectangleShape, RenderTarget, Transformable, Shape};
|
||||||
|
|
||||||
|
use crate::engine::renderer::Renderer;
|
||||||
|
use crate::engine::map::{CollisionTile, Map};
|
||||||
|
use crate::engine::texture::SPRITE_SIZE_I32;
|
||||||
|
use crate::engine::font::Font;
|
||||||
|
use crate::Result;
|
||||||
|
|
||||||
|
/// An action caused by a button.
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum Action {
|
||||||
|
/// Saves the level.
|
||||||
|
Save,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A button that can be clicked.
|
||||||
|
pub struct Button {
|
||||||
|
/// The label of the button.
|
||||||
|
pub label: String,
|
||||||
|
|
||||||
|
/// The position of the button.
|
||||||
|
pub position: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The size of the button.
|
||||||
|
pub size: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The action of the button.
|
||||||
|
action: Action,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Button {
|
||||||
|
/// Creates a new button.
|
||||||
|
pub fn new(label: &str, action: Action, x: f32, y: f32, w: f32, h: f32) -> Button {
|
||||||
|
Button {
|
||||||
|
action,
|
||||||
|
label: label.to_owned(),
|
||||||
|
position: Vector2::new(x, y),
|
||||||
|
size: Vector2::new(w, h),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the shape of the button.
|
||||||
|
pub fn shape(&self) -> RectangleShape {
|
||||||
|
let mut shape = RectangleShape::new();
|
||||||
|
shape.set_position(self.position + 0.1 * self.size);
|
||||||
|
shape.set_size(self.size - 0.2 * self.size);
|
||||||
|
shape.set_fill_color(&Color::rgb(255, 255, 255));
|
||||||
|
shape
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Draws the button on the renderer.
|
||||||
|
pub fn render_on(&self, renderer: &mut Renderer) {
|
||||||
|
renderer.window_mut().draw(&self.shape());
|
||||||
|
renderer.draw_text(
|
||||||
|
&self.label,
|
||||||
|
Font::Sansation,
|
||||||
|
self.size.y as u32 / 2,
|
||||||
|
&Color::rgb(0, 0, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns true if the coordinates fall inside the shape of the button.
|
||||||
|
pub fn contains(&self, x: f32, y: f32) -> bool {
|
||||||
|
let shape = self.shape();
|
||||||
|
x > shape.position().x && x < shape.position().x + shape.size().x &&
|
||||||
|
y > shape.position().y && y < shape.position().y + shape.size().y
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents a level editor.
|
||||||
|
pub struct Editor {
|
||||||
|
/// The renderer used by the editor.
|
||||||
|
renderer: Renderer,
|
||||||
|
|
||||||
|
/// The map being currently edited.
|
||||||
|
map: Map,
|
||||||
|
|
||||||
|
/// Indicates whether the editor is running or not.
|
||||||
|
running: bool,
|
||||||
|
|
||||||
|
/// The size of the left panel.
|
||||||
|
left_panel_size: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The size of the top panel.
|
||||||
|
top_panel_size: Vector2<f32>,
|
||||||
|
|
||||||
|
/// The buttons of the menu bar.
|
||||||
|
menu_bar: Vec<Button>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Editor {
|
||||||
|
/// Creates a new editor.
|
||||||
|
pub fn new() -> Editor {
|
||||||
|
let renderer = Renderer::new(800, 600, false);
|
||||||
|
let menu_bar = vec![
|
||||||
|
Button::new("Save", Action::Save, 0.0, 0.0, 50.0, 50.0),
|
||||||
|
];
|
||||||
|
|
||||||
|
Editor {
|
||||||
|
map: Map::new(50, 50),
|
||||||
|
running: true,
|
||||||
|
top_panel_size: Vector2::new(renderer.window().size().x as f32, 50.0),
|
||||||
|
left_panel_size: Vector2::new(100.0, renderer.window().size().y as f32 - 50.0),
|
||||||
|
renderer,
|
||||||
|
menu_bar,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Runs the main loop of the editor.
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
while self.running {
|
||||||
|
while let Some(event) = self.renderer.poll_event() {
|
||||||
|
self.manage_event(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.render();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Manages the event occuring on the display.
|
||||||
|
pub fn manage_event(&mut self, event: Event) {
|
||||||
|
|
||||||
|
match event {
|
||||||
|
// Quit the game if window is closed
|
||||||
|
Event::Closed => self.running = false,
|
||||||
|
|
||||||
|
// Quit the game if escape is pressed
|
||||||
|
Event::KeyPressed {
|
||||||
|
code: Key::Escape, ..
|
||||||
|
} => self.running = false,
|
||||||
|
|
||||||
|
// A click on the screen
|
||||||
|
Event::MouseButtonPressed {
|
||||||
|
button: MouseButton::Left, x, y,
|
||||||
|
} => {
|
||||||
|
if x as f32 >= self.left_panel_size.x && y as f32 >= self.top_panel_size.y {
|
||||||
|
let x = ((x - self.left_panel_size.x as i32) / SPRITE_SIZE_I32) as usize;
|
||||||
|
let y = ((y - self.top_panel_size.y as i32) / SPRITE_SIZE_I32) as usize;
|
||||||
|
|
||||||
|
if let Some(tile) = self.map.collision_tile(y, x) {
|
||||||
|
if tile.is_empty() {
|
||||||
|
self.map.set_tile(y, x, CollisionTile::full());
|
||||||
|
} else {
|
||||||
|
self.map.set_tile(y, x, CollisionTile::empty());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut actions = vec![];
|
||||||
|
for button in &self.menu_bar {
|
||||||
|
if button.contains(x as f32, y as f32) {
|
||||||
|
actions.push(button.action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for action in actions {
|
||||||
|
if let Err(e) = self.apply_action(action) {
|
||||||
|
eprintln!("warning: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the rendering on the screen.
|
||||||
|
pub fn render(&mut self) {
|
||||||
|
|
||||||
|
self.renderer.clear();
|
||||||
|
|
||||||
|
// Show the top panel
|
||||||
|
let mut top_panel = RectangleShape::with_size(self.top_panel_size);
|
||||||
|
top_panel.set_fill_color(&Color::rgb(255, 0, 0));
|
||||||
|
self.renderer.window_mut().draw(&top_panel);
|
||||||
|
|
||||||
|
for button in &self.menu_bar {
|
||||||
|
button.render_on(&mut self.renderer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the left panel
|
||||||
|
let mut left_panel = RectangleShape::with_size(self.left_panel_size);
|
||||||
|
left_panel.set_position((0.0, self.top_panel_size.y));
|
||||||
|
left_panel.set_fill_color(&Color::rgb(0, 255, 0));
|
||||||
|
self.renderer.window_mut().draw(&left_panel);
|
||||||
|
|
||||||
|
// Show the map
|
||||||
|
for i in 0 .. self.map.rows() {
|
||||||
|
for j in 0 .. self.map.cols() {
|
||||||
|
let tile = self.map.at(i, j);
|
||||||
|
if tile.graphic.is_visible() {
|
||||||
|
self.renderer.translate_and_draw(
|
||||||
|
&tile,
|
||||||
|
(self.left_panel_size.x, self.top_panel_size.y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the border of the map
|
||||||
|
let mut border = RectangleShape::new();
|
||||||
|
border.set_size(((
|
||||||
|
self.map.cols() * SPRITE_SIZE_I32 as usize) as f32,
|
||||||
|
(self.map.rows() * SPRITE_SIZE_I32 as usize) as f32
|
||||||
|
));
|
||||||
|
border.set_position((self.left_panel_size.x, self.top_panel_size.y));
|
||||||
|
border.set_fill_color(&Color::rgba(0, 0, 0, 0));
|
||||||
|
border.set_outline_color(&Color::rgb(0, 0, 0));
|
||||||
|
border.set_outline_thickness(2.0);
|
||||||
|
self.renderer.window_mut().draw(&border);
|
||||||
|
|
||||||
|
// Display and manage the frame rate
|
||||||
|
self.renderer.display();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Saves the level.
|
||||||
|
pub fn export(&self) -> Result<()> {
|
||||||
|
self.map.save("level.lvl")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Applies the corresponding action.
|
||||||
|
pub fn apply_action(&mut self, action: Action) -> Result<()> {
|
||||||
|
match action {
|
||||||
|
Action::Save => self.export()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,13 +1,13 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use clap::{crate_version, App, Arg};
|
use clap::{App, Arg, crate_version};
|
||||||
|
|
||||||
use sfml::window::{joystick, Event, Key};
|
use sfml::window::{joystick, Event, Key};
|
||||||
|
|
||||||
use rusty::engine::character::Character;
|
use rusty::engine::character::Character;
|
||||||
use rusty::engine::controls::Controls;
|
use rusty::engine::controls::Controls;
|
||||||
use rusty::engine::map::Map;
|
|
||||||
use rusty::engine::renderer::Renderer;
|
use rusty::engine::renderer::Renderer;
|
||||||
|
use rusty::engine::map::Map;
|
||||||
use rusty::engine::scene::{Scene, State};
|
use rusty::engine::scene::{Scene, State};
|
||||||
|
|
||||||
fn parse_resolution(res: &str) -> Result<(u32, u32), String> {
|
fn parse_resolution(res: &str) -> Result<(u32, u32), String> {
|
||||||
|
@ -33,30 +33,24 @@ fn parse_resolution(res: &str) -> Result<(u32, u32), String> {
|
||||||
fn main() {
|
fn main() {
|
||||||
let matches = App::new("Rusty Maker")
|
let matches = App::new("Rusty Maker")
|
||||||
.version(crate_version!())
|
.version(crate_version!())
|
||||||
.arg(
|
.arg(Arg::with_name("resolution")
|
||||||
Arg::with_name("resolution")
|
|
||||||
.short("r")
|
.short("r")
|
||||||
.long("resolution")
|
.long("resolution")
|
||||||
.value_name("WIDTHxHEIGHT")
|
.value_name("WIDTHxHEIGHT")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value("800x450")
|
.default_value("800x450")
|
||||||
.validator(|x| parse_resolution(&x).map(|_| ())),
|
.validator(|x| parse_resolution(&x).map(|_| ())))
|
||||||
)
|
.arg(Arg::with_name("map")
|
||||||
.arg(
|
|
||||||
Arg::with_name("map")
|
|
||||||
.short("m")
|
.short("m")
|
||||||
.long("map")
|
.long("map")
|
||||||
.value_name("MAP")
|
.value_name("MAP")
|
||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.default_value("assets/levels/level2.lvl")
|
.default_value("assets/levels/level2.lvl")
|
||||||
.validator(|x| Map::from_file(x).map(|_| ()).map_err(|x| format!("{}", x))),
|
.validator(|x| Map::load(x).map(|_| ()).map_err(|x| format!("{}", x))))
|
||||||
)
|
.arg(Arg::with_name("fullscreen")
|
||||||
.arg(
|
|
||||||
Arg::with_name("fullscreen")
|
|
||||||
.short("f")
|
.short("f")
|
||||||
.long("fullscreen")
|
.long("fullscreen")
|
||||||
.takes_value(false),
|
.takes_value(false))
|
||||||
)
|
|
||||||
.get_matches();
|
.get_matches();
|
||||||
|
|
||||||
let resolution = parse_resolution(matches.value_of("resolution").unwrap()).unwrap();
|
let resolution = parse_resolution(matches.value_of("resolution").unwrap()).unwrap();
|
||||||
|
@ -74,7 +68,7 @@ fn main() {
|
||||||
let mut character = Character::with_controls(controls);
|
let mut character = Character::with_controls(controls);
|
||||||
character.set_position((10.0, 0.0));
|
character.set_position((10.0, 0.0));
|
||||||
|
|
||||||
let map = Map::from_file(matches.value_of("map").unwrap()).unwrap();
|
let map = Map::load(matches.value_of("map").unwrap()).unwrap();
|
||||||
let mut scene = Scene::from_map(map);
|
let mut scene = Scene::from_map(map);
|
||||||
scene.add(character);
|
scene.add(character);
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
/// This module contains everything related to the level editor.
|
||||||
|
pub mod editor;
|
|
@ -5,10 +5,10 @@ use sfml::system::Vector2;
|
||||||
use sfml::window::Event;
|
use sfml::window::Event;
|
||||||
|
|
||||||
use crate::engine::controls::{Action, Controls};
|
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::math::{clamp, duration_as_f32, duration_as_frame};
|
||||||
use crate::engine::physics;
|
use crate::engine::physics;
|
||||||
use crate::engine::renderer::Drawable;
|
use crate::engine::renderer::Drawable;
|
||||||
use crate::engine::scene::Updatable;
|
|
||||||
use crate::engine::texture::Texture;
|
use crate::engine::texture::Texture;
|
||||||
|
|
||||||
/// The different sides a character can face.
|
/// The different sides a character can face.
|
||||||
|
@ -50,8 +50,8 @@ impl Side {
|
||||||
/// Returns the offset in the texture.
|
/// Returns the offset in the texture.
|
||||||
pub fn offset(&self) -> i32 {
|
pub fn offset(&self) -> i32 {
|
||||||
match *self {
|
match *self {
|
||||||
Side::Left => 32,
|
Side::Left => 0,
|
||||||
Side::Right => 0,
|
Side::Right => 32,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,10 +84,7 @@ pub struct Character {
|
||||||
max_jump: usize,
|
max_jump: usize,
|
||||||
|
|
||||||
/// The timer of the character's animation.
|
/// The timer of the character's animation.
|
||||||
animation_timer: Instant,
|
animation_timer: Option<Instant>,
|
||||||
|
|
||||||
/// Whether the character is walking or not.
|
|
||||||
walking: bool,
|
|
||||||
|
|
||||||
/// Indicates that the player has released the jump button.
|
/// Indicates that the player has released the jump button.
|
||||||
can_jump: bool,
|
can_jump: bool,
|
||||||
|
@ -104,9 +101,8 @@ impl Character {
|
||||||
hp: 1,
|
hp: 1,
|
||||||
jump_counter: 1,
|
jump_counter: 1,
|
||||||
max_jump: 1,
|
max_jump: 1,
|
||||||
animation_timer: Instant::now(),
|
animation_timer: None,
|
||||||
can_jump: true,
|
can_jump: true,
|
||||||
walking: false,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +153,7 @@ impl Character {
|
||||||
|
|
||||||
/// Returns the collision bounding box of the character.
|
/// Returns the collision bounding box of the character.
|
||||||
pub fn bbox(&self) -> FloatRect {
|
pub fn bbox(&self) -> FloatRect {
|
||||||
FloatRect::new(self.position.x + 8.0, self.position.y, 16.0, 32.0)
|
FloatRect::new(self.position.x + 8.0, self.position.y + 16.0, 16.0, 16.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the number of hp of the character.
|
/// Returns the number of hp of the character.
|
||||||
|
@ -194,16 +190,13 @@ impl Updatable for Character {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(side) = Side::from_force(force) {
|
if let Some(side) = Side::from_force(force) {
|
||||||
if !self.walking {
|
|
||||||
self.animation_timer = Instant::now();
|
|
||||||
}
|
|
||||||
self.walking = true;
|
|
||||||
self.side = side;
|
self.side = side;
|
||||||
} else {
|
|
||||||
if self.walking {
|
if self.animation_timer.is_none() {
|
||||||
self.animation_timer = Instant::now();
|
self.animation_timer = Some(Instant::now());
|
||||||
}
|
}
|
||||||
self.walking = false;
|
} else {
|
||||||
|
self.animation_timer = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let duration = duration_as_f32(duration);
|
let duration = duration_as_f32(duration);
|
||||||
|
@ -254,13 +247,17 @@ impl Updatable for Character {
|
||||||
|
|
||||||
impl Drawable for Character {
|
impl Drawable for Character {
|
||||||
fn texture(&self) -> Texture {
|
fn texture(&self) -> Texture {
|
||||||
Texture::Rusty
|
Texture::Mario
|
||||||
}
|
}
|
||||||
|
|
||||||
fn texture_rect(&self) -> IntRect {
|
fn texture_rect(&self) -> IntRect {
|
||||||
let frame = duration_as_frame(&Instant::now().duration_since(self.animation_timer), 4);
|
let frame = if let Some(started) = self.animation_timer {
|
||||||
let offset = if self.walking { 64 } else { 0 };
|
1 - duration_as_frame(&Instant::now().duration_since(started), 2)
|
||||||
IntRect::new(self.side.offset() + offset, frame * 32, 32, 32)
|
} else {
|
||||||
|
0
|
||||||
|
};
|
||||||
|
|
||||||
|
IntRect::new(self.side.offset(), frame * 32, 32, 32)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn position(&self) -> Vector2<f32> {
|
fn position(&self) -> Vector2<f32> {
|
||||||
|
|
|
@ -67,6 +67,7 @@ make_fonts!(
|
||||||
impl FontManager {
|
impl FontManager {
|
||||||
/// Creates a fonts from an array of bytes.
|
/// Creates a fonts from an array of bytes.
|
||||||
fn _make_font_from_bytes(bytes: Vec<u8>) -> SfFont {
|
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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,15 +1,19 @@
|
||||||
use std::fs::File;
|
|
||||||
use std::io::Read;
|
|
||||||
use std::path::Path;
|
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 sfml::graphics::{FloatRect, IntRect};
|
use sfml::graphics::{FloatRect, IntRect};
|
||||||
use sfml::system::Vector2;
|
use sfml::system::Vector2;
|
||||||
|
|
||||||
use crate::engine::character::Damage;
|
use crate::{Error, Result};
|
||||||
use crate::engine::math::{clamp, Matrix};
|
use crate::engine::math::{clamp, Matrix};
|
||||||
use crate::engine::renderer::Drawable;
|
use crate::engine::renderer::Drawable;
|
||||||
use crate::engine::texture::{byte_to_index, Texture, SPRITE_SIZE_F32, SPRITE_SIZE_I32};
|
use crate::engine::texture::{Texture, byte_to_index, SPRITE_SIZE_F32, SPRITE_SIZE_I32};
|
||||||
use crate::{Error, Result};
|
use crate::engine::character::Damage;
|
||||||
|
|
||||||
/// This enum represents if the collision happens on the X axis or the Y axis.
|
/// This enum represents if the collision happens on the X axis or the Y axis.
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone)]
|
||||||
|
@ -43,7 +47,7 @@ impl CollisionAxis {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct represents the different sides from which a collision can occur.
|
/// This struct represents the different sides from which a collision can occur.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct CollisionTile {
|
pub struct CollisionTile {
|
||||||
/// If the character comes from the top, it will collide if this bool is true.
|
/// If the character comes from the top, it will collide if this bool is true.
|
||||||
pub from_top: bool,
|
pub from_top: bool,
|
||||||
|
@ -91,10 +95,12 @@ impl CollisionTile {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This struct represents a renderable tile linking to its part in the tileset texture.
|
/// This struct represents a renderable tile linking to its part in the tileset texture.
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct GraphicTile(Option<i32>);
|
pub struct GraphicTile(Option<i32>);
|
||||||
|
|
||||||
impl GraphicTile {
|
impl GraphicTile {
|
||||||
|
|
||||||
|
|
||||||
/// Creates the correct graphic tile depending on the neighbours.
|
/// Creates the correct graphic tile depending on the neighbours.
|
||||||
///
|
///
|
||||||
/// A none will be considered solid.
|
/// A none will be considered solid.
|
||||||
|
@ -113,6 +119,7 @@ impl GraphicTile {
|
||||||
|
|
||||||
/// Creates the correct graphic tile depending on the neighbours.
|
/// Creates the correct graphic tile depending on the neighbours.
|
||||||
pub fn from_neighbours(tiles: &[CollisionTile; 8]) -> GraphicTile {
|
pub fn from_neighbours(tiles: &[CollisionTile; 8]) -> GraphicTile {
|
||||||
|
|
||||||
let mut byte = 0;
|
let mut byte = 0;
|
||||||
|
|
||||||
if !tiles[7].is_full() || !tiles[0].is_full() || !tiles[1].is_full() {
|
if !tiles[7].is_full() || !tiles[0].is_full() || !tiles[1].is_full() {
|
||||||
|
@ -154,7 +161,7 @@ impl GraphicTile {
|
||||||
pub fn offset(self) -> (i32, i32) {
|
pub fn offset(self) -> (i32, i32) {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
None => (0, 0),
|
None => (0, 0),
|
||||||
Some(v) => (32 * v, 0),
|
Some(v) => (32 * v, 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,6 +198,23 @@ 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.
|
/// The map represents the tiles contained in a level.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Map {
|
pub struct Map {
|
||||||
|
@ -217,69 +241,39 @@ impl Map {
|
||||||
Map::from_collision_tiles(tiles)
|
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.
|
/// Loads a map from a file.
|
||||||
pub fn from_file<P: AsRef<Path>>(path: P) -> Result<Map> {
|
pub fn load<P: AsRef<Path>>(path: P) -> Result<Map> {
|
||||||
let mut file = File::open(path.as_ref()).map_err(Error::Load)?;
|
let file = File::open(path.as_ref()).map_err(Error::Load)?;
|
||||||
let mut s = String::new();
|
let mut reader = BufReader::new(file);
|
||||||
file.read_to_string(&mut s).map_err(Error::Load)?;
|
Ok(deserialize_from(&mut reader).map(SaveMap::to_map).map_err(Error::Decoding)?)
|
||||||
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.
|
/// Creates a map from its entrance and collision tiles.
|
||||||
pub fn from_entrance_and_collision_tiles(e: (usize, usize), t: Matrix<CollisionTile>) -> Map {
|
pub fn from_entrance_and_collision_tiles(e: (usize, usize), t: Matrix<CollisionTile>) -> Map {
|
||||||
let rows = t.rows();
|
let rows = t.rows();
|
||||||
|
@ -311,34 +305,14 @@ impl Map {
|
||||||
/// Creates the neighbours of a tile.
|
/// Creates the neighbours of a tile.
|
||||||
pub fn neighbours(&self, i: usize, j: usize) -> [Option<CollisionTile>; 8] {
|
pub fn neighbours(&self, i: usize, j: usize) -> [Option<CollisionTile>; 8] {
|
||||||
[
|
[
|
||||||
if i > 0 && j > 0 {
|
if i > 0 && j > 0 { self.collision_tiles.get(i - 1, j - 1).cloned() } else { None },
|
||||||
self.collision_tiles.get(i - 1, j - 1).cloned()
|
if i > 0 { self.collision_tiles.get(i - 1, j ).cloned() } else { None },
|
||||||
} else {
|
if i > 0 { self.collision_tiles.get(i - 1, j + 1).cloned() } else { None },
|
||||||
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 , j + 1).cloned(),
|
||||||
self.collision_tiles.get(i + 1, j + 1).cloned(),
|
self.collision_tiles.get(i + 1, j + 1).cloned(),
|
||||||
self.collision_tiles.get(i + 1, j ).cloned(),
|
self.collision_tiles.get(i + 1, j ).cloned(),
|
||||||
if j > 0 {
|
if j > 0 { self.collision_tiles.get(i + 1, j - 1).cloned() } else { None },
|
||||||
self.collision_tiles.get(i + 1, j - 1).cloned()
|
if j > 0 { self.collision_tiles.get(i , j - 1).cloned() } else { None },
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
if j > 0 {
|
|
||||||
self.collision_tiles.get(i, j - 1).cloned()
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
},
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -410,6 +384,7 @@ impl Map {
|
||||||
old: FloatRect,
|
old: FloatRect,
|
||||||
new: FloatRect,
|
new: FloatRect,
|
||||||
) -> Option<(CollisionAxis, Vector2<f32>, Damage)> {
|
) -> Option<(CollisionAxis, Vector2<f32>, Damage)> {
|
||||||
|
|
||||||
let mut damage = Damage::None;
|
let mut damage = Damage::None;
|
||||||
|
|
||||||
let cols = self.collision_tiles.cols() - 1;
|
let cols = self.collision_tiles.cols() - 1;
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
use std::ops::{Index, IndexMut};
|
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
use std::ops::{Index, IndexMut};
|
||||||
|
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
/// Clamp a number between two boundaries.
|
/// Clamp a number between two boundaries.
|
||||||
pub fn clamp(number: f32, min: f32, max: f32) -> f32 {
|
pub fn clamp(number: f32, min: f32, max: f32) -> f32 {
|
||||||
|
@ -16,7 +18,7 @@ pub fn clamp(number: f32, min: f32, max: f32) -> f32 {
|
||||||
pub fn duration_as_frame(duration: &Duration, total: usize) -> i32 {
|
pub fn duration_as_frame(duration: &Duration, total: usize) -> i32 {
|
||||||
let secs = duration_as_f32(duration);
|
let secs = duration_as_f32(duration);
|
||||||
|
|
||||||
(secs * 10.0) as i32 % total as i32
|
(secs * 4.0) as i32 % total as i32
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Converts a duration into its number of seconds.
|
/// Converts a duration into its number of seconds.
|
||||||
|
@ -25,7 +27,7 @@ pub fn duration_as_f32(duration: &Duration) -> f32 {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A generic matrix type, useful for levels.
|
/// A generic matrix type, useful for levels.
|
||||||
#[derive(Clone)]
|
#[derive(Serialize, Deserialize, Clone)]
|
||||||
pub struct Matrix<T> {
|
pub struct Matrix<T> {
|
||||||
/// The number of rows of the matrix.
|
/// The number of rows of the matrix.
|
||||||
rows: usize,
|
rows: usize,
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
|
|
||||||
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, Text, View};
|
use sfml::graphics::{Color, IntRect, RenderTarget, RenderWindow, Sprite, View, Text};
|
||||||
|
|
||||||
use sfml::window::{Event, Style};
|
use sfml::window::{Event, Style};
|
||||||
|
|
||||||
use sfml::system::Vector2;
|
use sfml::system::Vector2;
|
||||||
|
|
||||||
use crate::engine::font::{Font, FontManager};
|
|
||||||
use crate::engine::scene::Scene;
|
use crate::engine::scene::Scene;
|
||||||
use crate::engine::texture::{Texture, TextureManager};
|
use crate::engine::texture::{Texture, TextureManager};
|
||||||
|
use crate::engine::font::{Font, FontManager};
|
||||||
|
|
||||||
/// Our custom drawable trait.
|
/// Our custom drawable trait.
|
||||||
pub trait Drawable {
|
pub trait Drawable {
|
||||||
|
@ -80,11 +80,7 @@ impl Renderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Draws a drawable with a certain translation.
|
/// Draws a drawable with a certain translation.
|
||||||
pub fn translate_and_draw<D: Drawable, V: Into<Vector2<f32>>>(
|
pub fn translate_and_draw<D: Drawable, V: Into<Vector2<f32>>>(&mut self, drawable: &D, translation: V) {
|
||||||
&mut self,
|
|
||||||
drawable: &D,
|
|
||||||
translation: V,
|
|
||||||
) {
|
|
||||||
let texture = self.texture_manager.get(drawable.texture());
|
let texture = self.texture_manager.get(drawable.texture());
|
||||||
let mut sprite = Sprite::with_texture(&texture);
|
let mut sprite = Sprite::with_texture(&texture);
|
||||||
sprite.set_texture_rect(&drawable.texture_rect());
|
sprite.set_texture_rect(&drawable.texture_rect());
|
||||||
|
|
|
@ -4,9 +4,9 @@ use sfml::graphics::View;
|
||||||
use sfml::system::Vector2;
|
use sfml::system::Vector2;
|
||||||
use sfml::window::Event;
|
use sfml::window::Event;
|
||||||
|
|
||||||
|
use crate::engine::texture::SPRITE_SIZE_F32;
|
||||||
use crate::engine::character::Character;
|
use crate::engine::character::Character;
|
||||||
use crate::engine::map::Map;
|
use crate::engine::map::Map;
|
||||||
use crate::engine::texture::SPRITE_SIZE_F32;
|
|
||||||
|
|
||||||
/// Contains everything needed to play.
|
/// Contains everything needed to play.
|
||||||
pub struct Scene {
|
pub struct Scene {
|
||||||
|
@ -86,11 +86,14 @@ impl Scene {
|
||||||
|
|
||||||
/// Updates the whole scene.
|
/// Updates the whole scene.
|
||||||
pub fn update(&mut self, duration: &Duration) -> State {
|
pub fn update(&mut self, duration: &Duration) -> State {
|
||||||
|
|
||||||
let mut state = State::Finished;
|
let mut state = State::Finished;
|
||||||
|
|
||||||
for c in &mut self.characters {
|
for c in &mut self.characters {
|
||||||
|
|
||||||
// Don't need to update if the character is dead
|
// Don't need to update if the character is dead
|
||||||
if c.is_alive() {
|
if c.is_alive() {
|
||||||
|
|
||||||
let old = c.bbox();
|
let old = c.bbox();
|
||||||
|
|
||||||
// Compute the offset between position and bbox
|
// Compute the offset between position and bbox
|
||||||
|
@ -109,6 +112,7 @@ impl Scene {
|
||||||
}
|
}
|
||||||
|
|
||||||
c.take_damage(damage);
|
c.take_damage(damage);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
c.fall_off();
|
c.fall_off();
|
||||||
}
|
}
|
||||||
|
@ -117,6 +121,7 @@ impl Scene {
|
||||||
if c.controls().is_some() {
|
if c.controls().is_some() {
|
||||||
state = State::Running;
|
state = State::Running;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -120,10 +120,10 @@ macro_rules! make_textures {
|
||||||
}
|
}
|
||||||
|
|
||||||
make_textures!(
|
make_textures!(
|
||||||
Rusty,
|
Mario,
|
||||||
rusty,
|
mario,
|
||||||
make_rusty_texture,
|
make_mario_texture,
|
||||||
"../../../assets/textures/rusty.png",
|
"../../../assets/textures/mario.png",
|
||||||
256,
|
256,
|
||||||
256,
|
256,
|
||||||
Overworld,
|
Overworld,
|
||||||
|
|
13
src/lib.rs
13
src/lib.rs
|
@ -2,11 +2,14 @@
|
||||||
|
|
||||||
//! This crates contains the (future) rusty game.
|
//! This crates contains the (future) rusty game.
|
||||||
|
|
||||||
use std::{fmt, io, result};
|
use std::{io, fmt, result};
|
||||||
|
|
||||||
/// This module contains all the tools needed for the game.
|
/// This module contains all the tools needed for the game.
|
||||||
pub mod engine;
|
pub mod engine;
|
||||||
|
|
||||||
|
/// This module contains all the applications of the game.
|
||||||
|
pub mod app;
|
||||||
|
|
||||||
/// This is the error type of this library.
|
/// This is the error type of this library.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -15,6 +18,12 @@ pub enum Error {
|
||||||
|
|
||||||
/// An error occured while trying to load a file.
|
/// An error occured while trying to load a file.
|
||||||
Load(io::Error),
|
Load(io::Error),
|
||||||
|
|
||||||
|
/// An error occured while encoding a file.
|
||||||
|
Encoding(bincode::Error),
|
||||||
|
|
||||||
|
/// An error occured while decoding a file.
|
||||||
|
Decoding(bincode::Error),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
impl fmt::Display for Error {
|
||||||
|
@ -22,6 +31,8 @@ impl fmt::Display for Error {
|
||||||
match *self {
|
match *self {
|
||||||
Error::Save(ref e) => write!(fmt, "couldn't save file: {}", e),
|
Error::Save(ref e) => write!(fmt, "couldn't save file: {}", e),
|
||||||
Error::Load(ref e) => write!(fmt, "couldn't load file: {}", e),
|
Error::Load(ref e) => write!(fmt, "couldn't load file: {}", e),
|
||||||
|
Error::Encoding(ref e) => write!(fmt, "couldn't encode file: {}", e),
|
||||||
|
Error::Decoding(ref e) => write!(fmt, "couldn't decode file: {}", e),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue