diff --git a/src/lib.rs b/src/lib.rs index 36a0933..9796082 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,6 +2,7 @@ pub mod bvh; pub mod map; pub mod aabb; pub mod model; +pub mod timer; pub mod zeroes; pub mod integer; pub mod updatable; @@ -9,4 +10,4 @@ pub mod instruction; pub mod gameplay_attributes; pub mod gameplay_modes; pub mod gameplay_style; -pub mod controls_bitflag; \ No newline at end of file +pub mod controls_bitflag; diff --git a/src/timer.rs b/src/timer.rs new file mode 100644 index 0000000..1255d48 --- /dev/null +++ b/src/timer.rs @@ -0,0 +1,292 @@ +use crate::integer::{Time,Ratio64}; + +#[derive(Clone,Copy,Debug)] +pub struct Paused; +#[derive(Clone,Copy,Debug)] +pub struct Unpaused; + +pub trait PauseState:Copy+std::fmt::Debug{ + const IS_PAUSED:bool; + fn new()->Self; +} +impl PauseState for Paused{ + const IS_PAUSED:bool=true; + fn new()->Self{ + Self + } +} +impl PauseState for Unpaused{ + const IS_PAUSED:bool=false; + fn new()->Self{ + Self + } +} + +#[derive(Clone,Copy,Debug)] +pub struct Realtime{ + offset:Time, +} +impl Realtime{ + const fn new(offset:Time)->Self{ + Self{offset} + } +} + +#[derive(Clone,Copy,Debug)] +pub struct Scaled{ + scale:Ratio64, + offset:Time, +} +impl Scaled{ + const fn new(scale:Ratio64,offset:Time)->Self{ + Self{scale,offset} + } + const fn with_scale(scale:Ratio64)->Self{ + Self{scale,offset:Time::ZERO} + } + const fn scale(&self,time:Time)->Time{ + Time::raw(self.scale.mul_int(time.get())) + } + const fn get_scale(&self)->Ratio64{ + self.scale + } + fn set_scale(&mut self,time:Time,new_scale:Ratio64){ + let new_time=self.get_time(time); + self.scale=new_scale; + self.set_time(time,new_time); + } +} + +pub trait TimerState:Copy+std::fmt::Debug{ + fn identity()->Self; + fn get_time(&self,time:Time)->Time; + fn set_time(&mut self,time:Time,new_time:Time); + fn get_offset(&self)->Time; + fn set_offset(&mut self,offset:Time); +} +impl TimerState for Realtime{ + fn identity()->Self{ + Self{offset:Time::ZERO} + } + fn get_time(&self,time:Time)->Time{ + time+self.offset + } + fn set_time(&mut self,time:Time,new_time:Time){ + self.offset=new_time-time; + } + fn get_offset(&self)->Time{ + self.offset + } + fn set_offset(&mut self,offset:Time){ + self.offset=offset; + } +} +impl TimerState for Scaled{ + fn identity()->Self{ + Self{scale:Ratio64::ONE,offset:Time::ZERO} + } + fn get_time(&self,time:Time)->Time{ + self.scale(time)+self.offset + } + fn set_time(&mut self,time:Time,new_time:Time){ + self.offset=new_time-self.scale(time); + } + fn get_offset(&self)->Time{ + self.offset + } + fn set_offset(&mut self,offset:Time){ + self.offset=offset; + } +} + +#[derive(Clone,Copy,Debug)] +pub struct TimerFixed{ + state:T, + _paused:P, +} + +//scaled timer methods are generic across PauseState +impl TimerFixed{ + pub fn scaled(time:Time,new_time:Time,scale:Ratio64)->Self{ + let mut timer=Self{ + state:Scaled::with_scale(scale), + _paused:P::new(), + }; + timer.set_time(time,new_time); + timer + } + pub const fn get_scale(&self)->Ratio64{ + self.state.get_scale() + } + pub fn set_scale(&mut self,time:Time,new_scale:Ratio64){ + self.state.set_scale(time,new_scale) + } +} + +//pause and unpause is generic across TimerState +impl TimerFixed{ + pub fn into_unpaused(self,time:Time)->TimerFixed{ + let new_time=self.time(time); + let mut timer=TimerFixed{ + state:self.state, + _paused:Unpaused, + }; + timer.set_time(time,new_time); + timer + } +} +impl TimerFixed{ + pub fn into_paused(self,time:Time)->TimerFixed{ + let new_time=self.time(time); + let mut timer=TimerFixed{ + state:self.state, + _paused:Paused, + }; + timer.set_time(time,new_time); + timer + } +} + +//the new constructor and time queries are generic across both +impl TimerFixed{ + pub fn new(time:Time,new_time:Time)->Self{ + let mut timer=Self{ + state:T::identity(), + _paused:P::new(), + }; + timer.set_time(time,new_time); + timer + } + pub fn from_state(state:T)->Self{ + Self{ + state, + _paused:P::new(), + } + } + pub fn into_state(self)->T{ + self.state + } + pub fn time(&self,time:Time)->Time{ + match P::IS_PAUSED{ + true=>self.state.get_offset(), + false=>self.state.get_time(time), + } + } + pub fn set_time(&mut self,time:Time,new_time:Time){ + match P::IS_PAUSED{ + true=>self.state.set_offset(new_time), + false=>self.state.set_time(time,new_time), + } + } +} + +#[derive(Debug)] +pub enum Error{ + AlreadyPaused, + AlreadyUnpaused, +} +impl std::fmt::Display for Error{ + fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{ + write!(f,"{self:?}") + } +} +impl std::error::Error for Error{} + +//wrapper type which holds type state internally +#[derive(Clone,Debug)] +pub enum Timer{ + Paused(TimerFixed), + Unpaused(TimerFixed), +} +impl Timer{ + pub fn from_state(state:T,paused:bool)->Self{ + match paused{ + true=>Self::Paused(TimerFixed::from_state(state)), + false=>Self::Unpaused(TimerFixed::from_state(state)), + } + } + pub fn into_state(self)->(T,bool){ + match self{ + Self::Paused(timer)=>(timer.into_state(),true), + Self::Unpaused(timer)=>(timer.into_state(),false), + } + } + pub fn paused(time:Time,new_time:Time)->Self{ + Self::Paused(TimerFixed::new(time,new_time)) + } + pub fn unpaused(time:Time,new_time:Time)->Self{ + Self::Unpaused(TimerFixed::new(time,new_time)) + } + pub fn time(&self,time:Time)->Time{ + match self{ + Self::Paused(timer)=>timer.time(time), + Self::Unpaused(timer)=>timer.time(time), + } + } + pub fn set_time(&mut self,time:Time,new_time:Time){ + match self{ + Self::Paused(timer)=>timer.set_time(time,new_time), + Self::Unpaused(timer)=>timer.set_time(time,new_time), + } + } + pub fn pause(&mut self,time:Time)->Result<(),Error>{ + *self=match *self{ + Self::Paused(_)=>return Err(Error::AlreadyPaused), + Self::Unpaused(timer)=>Self::Paused(timer.into_paused(time)), + }; + Ok(()) + } + pub fn unpause(&mut self,time:Time)->Result<(),Error>{ + *self=match *self{ + Self::Paused(timer)=>Self::Unpaused(timer.into_unpaused(time)), + Self::Unpaused(_)=>return Err(Error::AlreadyUnpaused), + }; + Ok(()) + } +} + +#[cfg(test)] +mod test{ + use super::{Time,Timer,TimerFixed,Error,Realtime,Scaled,Paused}; + macro_rules! sec { + ($s: expr) => { + Time::from_secs($s) + }; + } + #[test] + fn test_timerfixed_scaled(){ + //create a paused timer that reads 0s + let timer=TimerFixed::::new(sec!(0),sec!(0)); + //the paused timer at 1 second should read 0s + assert_eq!(timer.time(sec!(1)),sec!(0)); + + //unpause it after one second + let timer=timer.into_unpaused(sec!(1)); + //the timer at 6 seconds should read 5s + assert_eq!(timer.time(sec!(6)),sec!(5)); + + //pause the timer after 11 seconds + let timer=timer.into_paused(sec!(11)); + //the paused timer at 20 seconds should read 10s + assert_eq!(timer.time(sec!(20)),sec!(10)); + } + #[test] + fn test_timer()->Result<(),Error>{ + //create a paused timer that reads 0s + let mut timer=Timer::::paused(sec!(0),sec!(0)); + //the paused timer at 1 second should read 0s + assert_eq!(timer.time(sec!(1)),sec!(0)); + + //unpause it after one second + timer.unpause(sec!(1))?; + //the timer at 6 seconds should read 5s + assert_eq!(timer.time(sec!(6)),sec!(5)); + + //pause the timer after 11 seconds + timer.pause(sec!(11))?; + //the paused timer at 20 seconds should read 10s + assert_eq!(timer.time(sec!(20)),sec!(10)); + + Ok(()) + } +}