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, /// 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 { 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 { &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); }