use std::fmt; use std::str::FromStr; use anyhow::{anyhow, Result}; use clap::Clap; use log::{debug, info, trace, warn}; use crate::ddr::ssq; use crate::osu::beatmap; use crate::osu::types::*; #[derive(Clone, Debug)] pub struct ConfigRange(f32, f32); impl ConfigRange { /// Map value from 0 to 1 onto the range fn map_from(&self, value: f32) -> f32 { (value * (self.1 - self.0)) + self.0 } } impl fmt::Display for ConfigRange { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}:{}", self.0, self.1) } } impl FromStr for ConfigRange { type Err = anyhow::Error; fn from_str(string: &str) -> Result { match string.split(':').collect::>()[..] { [start, end] => Ok(ConfigRange(start.parse::()?, end.parse::()?)), _ => Err(anyhow!("Invalid range format (expected start:end)")), } } } #[derive(Debug, Clap, Clone)] pub struct Config { #[clap(skip = "audio.wav")] pub audio_filename: String, #[clap( long = "no-stops", about = "Disable stops", parse(from_flag = std::ops::Not::not), display_order = 3 )] pub stops: bool, #[clap( arg_enum, long, default_value = "step", about = "What to do with shocks", display_order = 3 )] pub shock_action: ShockAction, #[clap( long = "hp", about = "Range of HP drain (beginner:challenge)", default_value = "2:4" )] pub hp_drain: ConfigRange, #[clap( long = "acc", about = "Range of Accuracy (beginner:challenge)", default_value = "7:8" )] pub accuracy: ConfigRange, #[clap(flatten)] pub metadata: ConfigMetadata, } #[derive(Clap, Debug, Clone)] pub struct ConfigMetadata { #[clap(long, about = "Song title to use in beatmap", display_order = 4)] pub title: Option, #[clap(long, about = "Artist name to use in beatmap", display_order = 4)] pub artist: Option, #[clap( long, default_value = "Dance Dance Revolution", about = "Source to use in beatmap", display_order = 4 )] pub source: String, #[clap(skip)] pub levels: Option>, } impl fmt::Display for Config { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, "ddr2osu ({}shock→{:?} hp{} acc{})", if self.stops { "stops " } else { "" }, self.shock_action, self.hp_drain, self.accuracy ) } } #[derive(Clap, Clone, Debug)] pub enum ShockAction { Ignore, Step, //Static(Vec), } struct ShockStepGenerator { last: u8, columns: u8, mode: ShockAction, } impl Iterator for ShockStepGenerator { type Item = Vec; fn next(&mut self) -> Option> { match &self.mode { ShockAction::Ignore => None, ShockAction::Step => { let columns = match self.last { 0 | 3 => vec![0, 3], 1 | 2 => vec![1, 2], 4 | 7 => vec![4, 7], 5 | 6 => vec![5, 6], _ => vec![], }; self.last = (self.last + 1) % self.columns; Some(columns) } //ShockAction::Static(columns) => Some(columns.clone()), } } } impl ShockStepGenerator { fn new(columns: u8, mode: ShockAction) -> Self { Self { last: 0, columns, mode, } } } fn get_time_from_beats(beats: f32, tempo_changes: &ssq::TempoChanges) -> Option