2018-10-03 20:48:00 +02:00
|
|
|
use std::time::{
|
|
|
|
Instant,
|
|
|
|
Duration,
|
|
|
|
};
|
2018-10-02 21:48:19 +02:00
|
|
|
|
|
|
|
use sfml::system::Vector2;
|
|
|
|
use sfml::window::Event;
|
2018-10-03 20:48:00 +02:00
|
|
|
use sfml::window::Key;
|
2018-10-05 20:11:12 +02:00
|
|
|
use sfml::graphics::{
|
|
|
|
IntRect,
|
|
|
|
View,
|
|
|
|
};
|
2018-10-02 21:48:19 +02:00
|
|
|
|
|
|
|
use engine::scene::Updatable;
|
|
|
|
use engine::controls::Controls;
|
2018-10-03 20:48:00 +02:00
|
|
|
use engine::renderer::Drawable;
|
|
|
|
use engine::texture::Texture;
|
|
|
|
use engine::physics;
|
|
|
|
use engine::math::{
|
|
|
|
duration_as_f32,
|
|
|
|
duration_as_frame,
|
|
|
|
};
|
|
|
|
|
|
|
|
/// The different sides a character can face.
|
|
|
|
pub enum Side {
|
|
|
|
/// The character looks to the left.
|
|
|
|
Left,
|
|
|
|
|
|
|
|
/// The character looks to the right.
|
|
|
|
Right,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Side {
|
|
|
|
/// Returns the side corresponding to the force.
|
|
|
|
///
|
|
|
|
/// Returns None if the force is null.
|
|
|
|
pub fn from_force(force: Vector2<f32>) -> Option<Side> {
|
|
|
|
if force.x > 0.0 {
|
|
|
|
Some(Side::Right)
|
|
|
|
} else if force.x < 0.0 {
|
|
|
|
Some(Side::Left)
|
|
|
|
} else {
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns the offset in the texture.
|
|
|
|
pub fn offset(&self) -> i32 {
|
|
|
|
match *self {
|
|
|
|
Side::Left => 0,
|
|
|
|
Side::Right => 32,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-02 21:48:19 +02:00
|
|
|
|
|
|
|
/// A character, enemy or controllable.
|
|
|
|
pub struct Character {
|
|
|
|
|
|
|
|
/// The position of the character.
|
2018-10-03 20:48:00 +02:00
|
|
|
pub position: Vector2<f32>,
|
2018-10-02 21:48:19 +02:00
|
|
|
|
|
|
|
/// The speed of the character.
|
2018-10-03 20:48:00 +02:00
|
|
|
pub speed: Vector2<f32>,
|
2018-10-02 21:48:19 +02:00
|
|
|
|
|
|
|
/// If the player is controlling a character.
|
|
|
|
controls: Option<Controls>,
|
2018-10-03 20:48:00 +02:00
|
|
|
|
|
|
|
/// The side of the character.
|
|
|
|
side: Side,
|
|
|
|
|
|
|
|
/// The counter of jumps.
|
|
|
|
///
|
|
|
|
/// When it's 0, the character can no longer jump.
|
|
|
|
jump_counter: usize,
|
|
|
|
|
|
|
|
/// The maximum number of jumps a character can do.
|
|
|
|
///
|
|
|
|
/// It's reset when the character hits the ground.
|
|
|
|
max_jump: usize,
|
|
|
|
|
|
|
|
/// The timer of the character's animation.
|
|
|
|
animation_timer: Option<Instant>,
|
2018-10-04 21:47:29 +02:00
|
|
|
|
|
|
|
/// Indicates that the player has released the jump button.
|
|
|
|
can_jump: bool,
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Character {
|
2018-10-03 20:48:00 +02:00
|
|
|
|
2018-10-04 10:19:40 +02:00
|
|
|
/// Creates a character in (0, 0).
|
2018-10-03 20:48:00 +02:00
|
|
|
fn generic(controls: Option<Controls>) -> Character {
|
2018-10-02 21:48:19 +02:00
|
|
|
Character {
|
|
|
|
position: Vector2::new(0.0, 0.0),
|
|
|
|
speed: Vector2::new(0.0, 0.0),
|
2018-10-03 20:48:00 +02:00
|
|
|
controls: controls,
|
|
|
|
side: Side::Right,
|
|
|
|
jump_counter: 1,
|
|
|
|
max_jump: 1,
|
|
|
|
animation_timer: None,
|
2018-10-04 21:47:29 +02:00
|
|
|
can_jump: true,
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 20:48:00 +02:00
|
|
|
/// Creates a character in (0, 0).
|
|
|
|
pub fn new() -> Character {
|
|
|
|
Character::generic(None)
|
|
|
|
}
|
|
|
|
|
2018-10-02 21:48:19 +02:00
|
|
|
/// Creates a character with the specified controls.
|
|
|
|
pub fn with_controls(controls: Controls) -> Character {
|
2018-10-03 20:48:00 +02:00
|
|
|
Character::generic(Some(controls))
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Sets the position of the character.
|
|
|
|
pub fn set_position<S: Into<Vector2<f32>>>(&mut self, position: S) {
|
|
|
|
self.position = position.into();
|
|
|
|
}
|
2018-10-03 20:48:00 +02:00
|
|
|
|
|
|
|
/// Makes the character jump.
|
|
|
|
pub fn jump(&mut self) {
|
2018-10-04 21:47:29 +02:00
|
|
|
if self.can_jump && self.jump_counter > 0 {
|
2018-10-03 20:48:00 +02:00
|
|
|
self.jump_counter -= 1;
|
|
|
|
self.speed.y = physics::JUMP_SPEED.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Resets the jump counter.
|
|
|
|
pub fn ground_collision(&mut self) {
|
|
|
|
self.jump_counter = self.max_jump;
|
|
|
|
}
|
|
|
|
|
2018-10-04 10:19:40 +02:00
|
|
|
/// Make the player fall.
|
|
|
|
pub fn fall_off(&mut self) {
|
|
|
|
if self.jump_counter == self.max_jump {
|
|
|
|
self.jump_counter -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-05 20:11:12 +02:00
|
|
|
/// Returns a reference to the controls.
|
|
|
|
pub fn controls(&self) -> &Option<Controls> {
|
|
|
|
&self.controls
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a view that looks at the character.
|
|
|
|
pub fn view(&self) -> View {
|
|
|
|
View::new(self.position, Vector2::new(24.0 * 16.0, 24.0 * 9.0))
|
|
|
|
}
|
|
|
|
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Updatable for Character {
|
|
|
|
fn update(&mut self, duration: &Duration) {
|
|
|
|
|
2018-10-03 20:48:00 +02:00
|
|
|
let mut force: Vector2<f32> = Vector2::new(0.0, 0.0);
|
|
|
|
|
|
|
|
// Manage the input of the player
|
|
|
|
if Key::Left.is_pressed() {
|
|
|
|
force.x -= 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if Key::Right.is_pressed() {
|
|
|
|
force.x += 1.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if let Some(side) = Side::from_force(force) {
|
|
|
|
|
|
|
|
self.side = side;
|
|
|
|
|
|
|
|
if self.animation_timer.is_none() {
|
|
|
|
self.animation_timer = Some(Instant::now());
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
|
|
|
self.animation_timer = None;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let duration = duration_as_f32(duration);
|
|
|
|
|
|
|
|
// Compute acceleration
|
|
|
|
let accel = physics::G;
|
|
|
|
|
|
|
|
// Compute speed
|
|
|
|
self.speed.x *= physics::GROUND_FRICTION.x;
|
|
|
|
self.speed += accel * duration + force * 64.0;
|
|
|
|
|
|
|
|
// Compute position
|
|
|
|
self.position += self.speed * duration;
|
|
|
|
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
fn manage_event(&mut self, event: &Event) {
|
2018-10-03 20:48:00 +02:00
|
|
|
match event {
|
2018-10-02 21:48:19 +02:00
|
|
|
|
2018-10-03 20:48:00 +02:00
|
|
|
Event::KeyPressed {
|
|
|
|
code: Key::Return, ..
|
2018-10-04 21:47:29 +02:00
|
|
|
} => {
|
|
|
|
self.jump();
|
|
|
|
self.can_jump = false;
|
|
|
|
}
|
|
|
|
,
|
|
|
|
|
|
|
|
Event::KeyReleased {
|
|
|
|
code: Key::Return, ..
|
|
|
|
} => self.can_jump = true,
|
2018-10-03 20:48:00 +02:00
|
|
|
|
|
|
|
_ => (),
|
|
|
|
}
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Drawable for Character {
|
2018-10-03 20:48:00 +02:00
|
|
|
fn texture(&self) -> Texture {
|
|
|
|
Texture::Mario
|
|
|
|
}
|
|
|
|
|
|
|
|
fn texture_rect(&self) -> IntRect {
|
|
|
|
let frame = if let Some(started) = self.animation_timer {
|
|
|
|
duration_as_frame(&Instant::now().duration_since(started), 2)
|
2018-10-02 21:48:19 +02:00
|
|
|
} else {
|
2018-10-03 20:48:00 +02:00
|
|
|
0
|
|
|
|
};
|
|
|
|
|
|
|
|
IntRect::new(self.side.offset(), frame * 32, 32, 32)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn position(&self) -> Vector2<f32> {
|
|
|
|
self.position
|
2018-10-02 21:48:19 +02:00
|
|
|
}
|
|
|
|
}
|