From 0c4a34c9cb6398559351bfa85159f8eb2f2db1a2 Mon Sep 17 00:00:00 2001
From: Quaternions <krakow20@gmail.com>
Date: Wed, 31 Jul 2024 13:16:16 -0700
Subject: [PATCH] runs

---
 src/lib.rs |   1 +
 src/run.rs | 103 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 104 insertions(+)
 create mode 100644 src/run.rs

diff --git a/src/lib.rs b/src/lib.rs
index 9796082..ff3601a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1,5 +1,6 @@
 pub mod bvh;
 pub mod map;
+pub mod run;
 pub mod aabb;
 pub mod model;
 pub mod timer;
diff --git a/src/run.rs b/src/run.rs
new file mode 100644
index 0000000..1f6123a
--- /dev/null
+++ b/src/run.rs
@@ -0,0 +1,103 @@
+use crate::timer::{TimerFixed,Realtime,Paused,Unpaused};
+use crate::integer::Time;
+
+#[derive(Clone,Debug)]
+pub enum FlagReason{
+	Anticheat,
+	StyleChange,
+	Clock,
+	Pause,
+	Flying,
+	Gravity,
+	Timescale,
+	TimeTravel,
+	Teleport,
+}
+impl ToString for FlagReason{
+	fn to_string(&self)->String{
+		match self{
+			FlagReason::Anticheat=>"Passed through anticheat zone.",
+			FlagReason::StyleChange=>"Changed style.",
+			FlagReason::Clock=>"Incorrect clock.  (This can be caused by internet hiccups)",
+			FlagReason::Pause=>"Pausing is not allowed in this style.",
+			FlagReason::Flying=>"Flying is not allowed in this style.",
+			FlagReason::Gravity=>"Gravity modification is not allowed in this style.",
+			FlagReason::Timescale=>"Timescale is not allowed in this style.",
+			FlagReason::TimeTravel=>"Time travel is not allowed in this style.",
+			FlagReason::Teleport=>"Illegal teleport.",
+		}.to_owned()
+	}
+}
+
+#[derive(Debug)]
+pub enum Error{
+	NotStarted,
+	AlreadyStarted,
+	AlreadyFinished,
+}
+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{}
+
+#[derive(Clone,Debug)]
+enum RunState{
+	Created,
+	Started{timer:TimerFixed<Realtime,Unpaused>},
+	Finished{timer:TimerFixed<Realtime,Paused>},
+}
+
+#[derive(Clone,Debug)]
+pub struct Run{
+	state:RunState,
+	flagged:Option<FlagReason>,
+}
+
+impl Run{
+	pub fn new()->Self{
+		Self{
+			state:RunState::Created,
+			flagged:None,
+		}
+	}
+	pub fn time(&self,time:Time)->Time{
+		match &self.state{
+			RunState::Created=>Time::ZERO,
+			RunState::Started{timer}=>timer.time(time),
+			RunState::Finished{timer}=>timer.time(time),
+		}
+	}
+	pub fn start(&mut self,time:Time)->Result<(),Error>{
+		match &self.state{
+			RunState::Created=>{
+				self.state=RunState::Started{
+					timer:TimerFixed::new(time,Time::ZERO),
+				};
+				Ok(())
+			},
+			RunState::Started{..}=>Err(Error::AlreadyStarted),
+			RunState::Finished{..}=>Err(Error::AlreadyFinished),
+		}
+	}
+	pub fn finish(&mut self,time:Time)->Result<(),Error>{
+		//this uses Copy
+		match &self.state{
+			RunState::Created=>Err(Error::NotStarted),
+			RunState::Started{timer}=>{
+				self.state=RunState::Finished{
+					timer:timer.into_paused(time),
+				};
+				Ok(())
+			},
+			RunState::Finished{..}=>Err(Error::AlreadyFinished),
+		}
+	}
+	pub fn flag(&mut self,flag_reason:FlagReason){
+		//don't replace the first reason the run was flagged
+		if self.flagged.is_none(){
+			self.flagged=Some(flag_reason);
+		}
+	}
+}