organicomplex 0.7.0

Interactive complex-valued cellular automaton on 2D and 3D grids in search of that stuff - emergence, open-endedness, organicity etc.
use crate::base::PrependErrorString;

mod audio;
mod config;
mod input;
mod random;
mod time;
mod video;

mod sys_audio;
mod sys_input;
mod sys_random;
mod sys_time;
mod sys_video;

pub use self::{
    audio::{
        Music,
        Sound
    },
    input::{
        Button,
        Key
    },
    // video::Image
};

pub struct System {
    config: Box<self::config::Config>,

    time: Box<self::time::System>,
    random: Box<self::random::System>,

    // If the one from init() persists and is destroyed automatically, this is redundant
    // sdl: Box<sdl2::Sdl>,

    video: Box<self::video::System>,
    draw_suspended: bool,
    draw_locked: bool,
    last_show_time: i128,
    fps: Vec<f64>,
    fps_i: usize,

    audio: Box<self::audio::System>,

    input: Box<self::input::System>
}

impl System {
    pub fn init() -> Result<System, String> {
        let config = config::Config::load().pre_err("cannot load config")?;

        let time = time::System::init().pre_err("cannot init time subsys")?;
        
        let random = random::System::init(&config).pre_err("cannot init random subsys")?;

        let sdl = sdl2::init().pre_err("cannot init SDL")?;

        // Video first, it can change resolution in case of Fullscreen Desktop mode
        let video = video::System::init(&sdl, &config).pre_err("cannot init video subsys")?;

        let draw_suspended: bool = false;
        let draw_locked: bool = false;
        let last_show_time = time.now();
        let fps: Vec<f64> = vec![0.0; config.frame_rate as usize];
        let fps_i: usize = 0;

        let audio = audio::System::init(&config).pre_err("cannot init audio subsys")?;

        let input = input::System::init(&sdl, &config).pre_err("cannot init input subsys")?;

        Ok(System{
            config: Box::new(config),
            time: Box::new(time),
            random: Box::new(random),
            // sdl: Box::new(sdl),
            video: Box::new(video),
            draw_suspended,
            draw_locked,
            last_show_time,
            fps,
            fps_i,
            audio: Box::new(audio),
            input: Box::new(input)
        })
    }

    pub fn lang(&self) -> String {
        self.config.lang.clone()
    }

    pub fn fps(&self) -> f64 {
        self.fps.iter().sum::<f64>() / (self.config.frame_rate as f64)
    }

    pub fn sync(&mut self) -> Result<(), String> {
        self.draw_locked = true;

        let now = self.time.now();

        let delta = now - self.last_show_time;
        if !self.draw_suspended && (delta > 1000000i128 / (self.config.frame_rate as i128)) {
            self.video.show().pre_err("cannot sync video")?;

            self.fps[self.fps_i] = 1e6 / (delta as f64);
            self.fps_i = (self.fps_i + 1) % (self.config.frame_rate as usize);

            self.last_show_time = now;

            self.draw_locked = false;
        }

        self.input.sync().pre_err("cannot sync input")?;

        Ok(())
    }

    pub fn draw_suspended(&self) -> bool {
        self.draw_suspended
    }

    pub fn set_draw_suspend(&mut self, suspended: bool) {
        self.draw_suspended = suspended;
    }

    pub fn shut(&mut self) -> Result<(), String> {
        self.input.shut().pre_err("cannot shut input subsys")?;
        self.audio.shut().pre_err("cannot shut audio subsys")?;
        self.video.shut().pre_err("cannot shut video subsys")?;
        self.random.shut().pre_err("cannot shut random subsys")?;
        self.time.shut().pre_err("cannot shut time subsys")?;
        Ok(())
    }

}