free-rusty-maker/src/engine/scene/mod.rs

152 lines
4.2 KiB
Rust

use std::time::Duration;
use sfml::graphics::View;
use sfml::system::Vector2;
use sfml::window::Event;
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 {
/// The characters contained in the scene.
characters: Vec<Character>,
/// The map of the scene.
map: Map,
}
/// The type used to represent whether a a scene is running or finished.
#[derive(Copy, Clone, PartialEq, Eq)]
pub enum State {
/// The scene is running.
Running,
/// The scene is finished.
Finished,
}
impl Scene {
/// Creates a scene from a map level.
pub fn from_map(map: Map) -> Scene {
Scene {
characters: vec![],
map,
}
}
/// Adds a character to the scene.
pub fn add(&mut self, character: Character) {
let mut character = character;
character.position.x = self.map.entrance().1 as f32 * SPRITE_SIZE_F32;
character.position.y = self.map.entrance().0 as f32 * SPRITE_SIZE_F32;
self.characters.push(character);
}
/// Returns the controlable.
fn controlable(&self) -> Option<&Character> {
for character in &self.characters {
if character.controls().is_some() {
return Some(&character);
}
}
None
}
/// Returns the right view.
pub fn view(&self) -> Option<View> {
let view = self.controlable()?.view();
let mut center = view.center();
let size = view.size();
// Clamp center so that the view doesn't show things outside the level.
if center.x - size.x / 2.0 < 0.0 {
center.x = size.x / 2.0;
}
if center.y - size.y / 2.0 < 0.0 {
center.y = size.y / 2.0;
}
let right_limit = self.map.cols() as f32 * SPRITE_SIZE_F32;
let bottom_limit = self.map.rows() as f32 * SPRITE_SIZE_F32;
if center.x + size.x / 2.0 > right_limit {
center.x = right_limit - size.x / 2.0;
}
if center.y + size.y / 2.0 > bottom_limit {
center.y = bottom_limit - size.y / 2.0;
}
Some(View::new(center, size))
}
/// 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
let offset = Vector2::new(old.left, old.top) - c.position;
c.update(duration);
if let Some((axis, position, damage)) = self.map.collides_bbox(old, c.bbox()) {
c.position = position - offset;
if axis.is_x() {
c.speed.x = 0.0;
}
if axis.is_y() {
c.speed.y = 0.0;
c.ground_collision();
}
c.take_damage(damage);
} else {
c.fall_off();
}
// If a character is alive and still have controls, the game should continue
if c.controls().is_some() {
state = State::Running;
}
}
}
state
}
/// Transfers an event to the elements contained in the scene that should receive events.
pub fn manage_event(&mut self, event: &Event) {
for c in &mut self.characters {
c.manage_event(event);
}
}
/// Returns a reference to the characters of the scene.
pub fn characters(&self) -> &Vec<Character> {
&self.characters
}
/// Returns a reference to the map.
pub fn map(&self) -> &Map {
&self.map
}
}
/// Trait that needs to be implemented for everything that can be updatable.
pub trait Updatable {
/// Updates the thing depending on the duration since last frame.
fn update(&mut self, duration: &Duration);
/// Called when an event arrives.
fn manage_event(&mut self, event: &Event);
}