use serde_derive::{Deserialize, Serialize}; use std::fmt; use std::path::PathBuf; use std::slice::Iter; use std::cmp::Ordering; pub struct CommandManager { pub launcher: Option, pub iwad: Option, pub pwads: Vec, pub warp: String, pub difficulty: Difficulty, pub fast_monsters: bool, pub respawning_monsters: bool, pub command_string: String, } pub enum CommandErrors { NoLauncher, NoIwad, } impl Default for CommandManager { fn default() -> Self { Self { launcher: None, iwad: None, pwads: Vec::new(), warp: "".to_string(), difficulty: Difficulty::None, fast_monsters: false, respawning_monsters: false, command_string: "".to_string(), } } } impl CommandManager { pub fn remove_iwad(&mut self) { self.iwad = None; self.update_command(); } pub fn add_iwad(&mut self, iwad: &WadInfo) { self.iwad = Some(iwad.path.clone()); self.update_command(); } pub fn add_pwads(&mut self, pwads: &Vec<&WadInfo>) { self.pwads = Vec::new(); for pwad in pwads { self.pwads.push(pwad.path.clone()); } self.update_command(); } pub fn add_launcher(&mut self, launcher: &LauncherInfo) { self.launcher = Some(launcher.path.clone()); self.update_command(); } pub fn remove_launcher(&mut self) { self.launcher = None; self.update_command(); } pub fn generate_command(&self) -> Result<(String, Vec), CommandErrors> { let launcher = if let Some(launcher) = &self.launcher { launcher } else { return Err(CommandErrors::NoLauncher); }; let iwad = if let Some(iwad) = &self.iwad { iwad } else { return Err(CommandErrors::NoIwad); }; let mut command = vec!["-iwad".to_string(), iwad.clone().into_os_string().into_string().unwrap()]; for pwad in &self.pwads { command.push("-file".to_string()); command.push(pwad.clone().into_os_string().into_string().unwrap()); } if self.difficulty != Difficulty::None { command.push("-skill".to_string()); command.push(self.difficulty.flag_number().to_string()); } if !self.warp.is_empty() { command.push("-warp".to_string()); // Again, feel like there's must be a _much_ better way than this... let warp_string = self.warp.to_string(); let strings: Vec<&str> = warp_string.split_whitespace().collect(); for string in strings { command.push(string.to_string()); } } if self.respawning_monsters { command.push("-respawn".to_string()); } if self.fast_monsters { command.push("-fast".to_string()); } Ok((launcher.to_str().unwrap().to_string(), command)) } pub fn update_command(&mut self) { match self.generate_command() { Err(e) => { self.command_string = "plz add ".to_string(); match e { CommandErrors::NoIwad => self.command_string.push_str("an iwad"), CommandErrors::NoLauncher => self.command_string.push_str("a launcher"), } }, Ok((launcher, rest)) => { let mut command = "".to_string(); // Feels like a bit of a hack, but it works I think command.push_str(&format!(" '{}'", launcher)); for c in rest { command.push_str(&format!(" '{}'", c)); } self.command_string = command; } } } } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct WadInfo { pub path: PathBuf, pub name: String, pub info_text: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] pub struct LauncherInfo { pub path: PathBuf, pub name: String, } #[derive(Default)] pub struct MultiManager { pub selectable: Vec, pub current_selected: Vec, } impl MultiManager { pub fn new() -> Self { Self { selectable: Vec::new(), current_selected: Vec::new(), } } pub fn add(&mut self, item: &T) { self.selectable.push(item.clone()); } pub fn get(&self, index: usize) -> Option<(&T, bool)> { if index > self.selectable.len() { None } else { Some(( self.selectable.get(index).unwrap(), self.current_selected.contains(&index), )) } } pub fn get_current(&self) -> Vec<&T> { let mut current_selected_items = Vec::new(); for item in &self.current_selected { assert!(item < &self.selectable.len()); current_selected_items.push(self.selectable.get(*item).unwrap()); } current_selected_items } pub fn set_current(&mut self, index: usize) { assert!(index < self.selectable.len()); self.current_selected.push(index); } pub fn remove_current(&mut self, index: usize) { assert!(index < self.current_selected.len()); self.current_selected.remove(index); } pub fn remove_selectable(&mut self, index: usize) { assert!(index < self.selectable.len()); self.selectable.remove(index); let mut new_selected: Vec = Vec::new(); for s in &self.current_selected { match s.cmp(&index) { Ordering::Equal => continue, Ordering::Less => new_selected.push(*s), Ordering::Greater => new_selected.push(s - 1), } } self.current_selected = new_selected; } pub fn is_currently_selected(self, index: usize) -> bool { self.current_selected.contains(&index) } pub fn iter_selectable_with_pos_and_selected( &mut self, ) -> impl Iterator + '_ { self.selectable .iter_mut() .enumerate() .map(|(i, l)| (l, i, self.current_selected.contains(&i))) } } #[derive(Default)] pub struct SingleManager { pub selectable: Vec, pub current_selected: Option, } impl SingleManager { pub fn new() -> Self { Self { selectable: Vec::new(), current_selected: None, } } pub fn add(&mut self, item: &T) { self.selectable.push(item.clone()); } pub fn get(&self, index: usize) -> Option<(&T, bool)> { if index > self.selectable.len() { None } else { Some(( self.selectable.get(index).unwrap(), index == self.current_selected.unwrap_or(std::usize::MAX), )) } } pub fn get_current(&self) -> Option<&T> { match self.current_selected { None => None, Some(pos) => Some(self.selectable.get(pos).unwrap()), } } pub fn set_current(&mut self, index: usize) { assert!(index < self.selectable.len()); self.current_selected = Some(index); } pub fn remove_current(&mut self) { self.current_selected = None; } pub fn remove_selectable(&mut self, index: usize) { assert!(index < self.selectable.len()); self.selectable.remove(index); if self.current_selected.is_some() && index == self.current_selected.unwrap() { self.current_selected = None; } } pub fn iter_selectable_with_pos_and_selected( &mut self, ) -> impl Iterator + '_ { self.selectable .iter_mut() .enumerate() .map(|(i, l)| (l, i, i == self.current_selected.unwrap_or(std::usize::MAX))) } } #[derive(PartialEq, Clone, Copy)] pub enum Difficulty { None, Baby, Easy, Medium, Hard, Nightmare, } impl fmt::Display for Difficulty { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { Difficulty::Baby => write!(f, "I'm too young to die"), Difficulty::Easy => write!(f, "Hey, not too rough"), Difficulty::Medium => write!(f, "Hurt me plenty"), Difficulty::Hard => write!(f, "Ultra-Violence"), Difficulty::Nightmare => write!(f, "Nightmare!"), Difficulty::None => write!(f, "None"), } } } impl Difficulty { pub fn iterator() -> Iter<'static, Difficulty> { static DIFFICULTIES: [Difficulty; 6] = [ Difficulty::None, Difficulty::Baby, Difficulty::Easy, Difficulty::Medium, Difficulty::Hard, Difficulty::Nightmare, ]; DIFFICULTIES.iter() } pub fn flag_number(&self) -> &str { match self { Difficulty::None => "0", Difficulty::Baby => "1", Difficulty::Easy => "2", Difficulty::Medium => "3", Difficulty::Hard => "4", Difficulty::Nightmare => "5", } } }