diff --git a/README.md b/README.md index d785436..d6d3a7e 100644 --- a/README.md +++ b/README.md @@ -179,7 +179,24 @@ Its constructor takes [an object of the form](https://github.com/MaxOhn/rosu-pp- Its only method is `build(): BeatmapAttributes`. -## Example +## Setters + +For the `Difficulty`, `Performance`, and `BeatmapAttributesBuilder` classes, each field of their constructor's argument object is also available as a setter afterwards which takes either a value of the field's type or `undefined` to unset the previously set value. + +```js +import * as rosu from "rosu-pp-js"; + +// Either given in the constructor +let difficulty = new rosu.Difficulty({ clockRate: 1.23 }); + +// Or adjusted afterwards +difficulty.mods = 8; + +// Or even reset entirely +difficulty.clockRate = undefined; +``` + +## Examples ### Calculating performance diff --git a/rosu_pp_js.d.ts b/rosu_pp_js.d.ts index 64d0907..86beb2a 100644 --- a/rosu_pp_js.d.ts +++ b/rosu_pp_js.d.ts @@ -372,6 +372,45 @@ export class BeatmapAttributesBuilder { * @returns {BeatmapAttributes} */ build(): BeatmapAttributes; +/** +*/ + ar?: number; +/** +*/ + arWithMods?: boolean; +/** +*/ + clockRate?: number; +/** +*/ + cs?: number; +/** +*/ + csWithMods?: boolean; +/** +*/ + hp?: number; +/** +*/ + hpWithMods?: boolean; +/** +*/ + isConvert?: boolean; +/** +*/ + map?: Beatmap; +/** +*/ + mode?: GameMode; +/** +*/ + mods?: number; +/** +*/ + od?: number; +/** +*/ + odWithMods?: boolean; } /** * Builder for a difficulty calculation. @@ -410,6 +449,42 @@ export class Difficulty { * @returns {GradualPerformance} */ gradualPerformance(map: Beatmap): GradualPerformance; +/** +*/ + ar?: number; +/** +*/ + arWithMods?: boolean; +/** +*/ + clockRate?: number; +/** +*/ + cs?: number; +/** +*/ + csWithMods?: boolean; +/** +*/ + hardrockOffsets?: boolean; +/** +*/ + hp?: number; +/** +*/ + hpWithMods?: boolean; +/** +*/ + mods?: number; +/** +*/ + od?: number; +/** +*/ + odWithMods?: boolean; +/** +*/ + passedObjects?: number; } /** * The result of a difficulty calculation. @@ -655,6 +730,69 @@ export class Performance { * @returns {PerformanceAttributes} */ calculate(args: MapOrAttributes): PerformanceAttributes; +/** +*/ + accuracy?: number; +/** +*/ + ar?: number; +/** +*/ + arWithMods?: boolean; +/** +*/ + clockRate?: number; +/** +*/ + combo?: number; +/** +*/ + cs?: number; +/** +*/ + csWithMods?: boolean; +/** +*/ + hardrockOffsets?: boolean; +/** +*/ + hitresultPriority?: HitResultPriority; +/** +*/ + hp?: number; +/** +*/ + hpWithMods?: boolean; +/** +*/ + misses?: number; +/** +*/ + mods?: number; +/** +*/ + n100?: number; +/** +*/ + n300?: number; +/** +*/ + n50?: number; +/** +*/ + nGeki?: number; +/** +*/ + nKatu?: number; +/** +*/ + od?: number; +/** +*/ + odWithMods?: boolean; +/** +*/ + passedObjects?: number; } /** * The result of a performance calculation. diff --git a/src/args/beatmap.rs b/src/args/beatmap.rs index 9205302..023a34a 100644 --- a/src/args/beatmap.rs +++ b/src/args/beatmap.rs @@ -1,5 +1,6 @@ use std::fmt::{Formatter, Result as FmtResult}; +use rosu_pp::model::beatmap::BeatmapAttributesBuilder; use serde::de; use wasm_bindgen::{__rt::RefMut, prelude::wasm_bindgen}; @@ -101,6 +102,42 @@ pub struct BeatmapAttributesArgs { pub map: Option>, } +impl BeatmapAttributesArgs { + pub fn as_builder(&self) -> BeatmapAttributesBuilder { + let mut builder = BeatmapAttributesBuilder::new(); + + if let Some(ref map) = self.map { + builder = builder.map(&map.inner); + } + + if let Some(mode) = self.mode { + builder = builder.mode(mode.into(), self.is_convert); + } + + if let Some(clock_rate) = self.clock_rate { + builder = builder.clock_rate(clock_rate); + } + + if let Some(ar) = self.ar { + builder = builder.ar(ar, self.ar_with_mods); + } + + if let Some(cs) = self.cs { + builder = builder.cs(cs, self.cs_with_mods); + } + + if let Some(hp) = self.hp { + builder = builder.hp(hp, self.hp_with_mods); + } + + if let Some(od) = self.od { + builder = builder.od(od, self.od_with_mods); + } + + builder.mods(self.mods) + } +} + fn deser_maybe_map<'de, D: de::Deserializer<'de>>( d: D, ) -> Result>, D::Error> { diff --git a/src/args/difficulty.rs b/src/args/difficulty.rs index fdf2842..7e29f56 100644 --- a/src/args/difficulty.rs +++ b/src/args/difficulty.rs @@ -28,7 +28,7 @@ export interface DifficultyArgs extends CommonArgs { hardrockOffsets?: boolean; }"#; -#[derive(Default, serde::Deserialize)] +#[derive(Clone, Default, serde::Deserialize)] #[serde(rename_all = "camelCase", rename = "Object")] pub struct DifficultyArgs { #[serde(default)] diff --git a/src/args/performance.rs b/src/args/performance.rs index 83df5b9..5a36a55 100644 --- a/src/args/performance.rs +++ b/src/args/performance.rs @@ -119,6 +119,15 @@ pub enum JsHitResultPriority { WorstCase, } +impl From for HitResultPriority { + fn from(priority: JsHitResultPriority) -> Self { + match priority { + JsHitResultPriority::BestCase => Self::BestCase, + JsHitResultPriority::WorstCase => Self::WorstCase, + } + } +} + impl JsHitResultPriority { fn deserialize<'de, D: de::Deserializer<'de>>(d: D) -> Result { let priority = match ::deserialize(d) { diff --git a/src/attributes/beatmap.rs b/src/attributes/beatmap.rs index a8c0122..aa19b02 100644 --- a/src/attributes/beatmap.rs +++ b/src/attributes/beatmap.rs @@ -1,14 +1,23 @@ -use rosu_pp::model::beatmap::{BeatmapAttributes, BeatmapAttributesBuilder}; +use rosu_pp::model::beatmap::BeatmapAttributes; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ args::beatmap::{BeatmapAttributesArgs, JsBeatmapAttributesArgs}, - util, JsError, JsResult, + beatmap::JsBeatmap, + deserializer::JsDeserializer, + mode::JsGameMode, + util, JsResult, }; #[wasm_bindgen(js_name = BeatmapAttributesBuilder)] pub struct JsBeatmapAttributesBuilder { - inner: BeatmapAttributesBuilder, + args: BeatmapAttributesArgs, +} + +#[wasm_bindgen] +extern "C" { + #[wasm_bindgen(typescript_type = Beatmap)] + pub type JsBeatmapType; } #[wasm_bindgen(js_class = BeatmapAttributesBuilder)] @@ -16,19 +25,89 @@ impl JsBeatmapAttributesBuilder { /// Create a new `BeatmapAttributesBuilder`. #[wasm_bindgen(constructor)] pub fn new(args: Option) -> JsResult { - let inner = if let Some(ref args) = args { - util::from_value::(args) - .and_then(BeatmapAttributesBuilder::try_from)? - } else { - BeatmapAttributesBuilder::new() - }; + let args = args + .as_deref() + .map(util::from_value::) + .transpose()? + .unwrap_or_default(); - Ok(Self { inner }) + Ok(Self { args }) } /// Calculate the `BeatmapAttributes`. pub fn build(&self) -> JsBeatmapAttributes { - self.inner.build().into() + self.args.as_builder().build().into() + } + + #[wasm_bindgen(setter)] + pub fn set_mods(&mut self, mods: Option) { + self.args.mods = mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter = clockRate)] + pub fn set_clock_rate(&mut self, clock_rate: Option) { + self.args.clock_rate = clock_rate; + } + + #[wasm_bindgen(setter)] + pub fn set_ar(&mut self, ar: Option) { + self.args.ar = ar; + } + + #[wasm_bindgen(setter = arWithMods)] + pub fn set_ar_with_mods(&mut self, ar_with_mods: Option) { + self.args.ar_with_mods = ar_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_cs(&mut self, cs: Option) { + self.args.cs = cs; + } + + #[wasm_bindgen(setter = csWithMods)] + pub fn set_cs_with_mods(&mut self, cs_with_mods: Option) { + self.args.cs_with_mods = cs_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_hp(&mut self, hp: Option) { + self.args.hp = hp; + } + + #[wasm_bindgen(setter = hpWithMods)] + pub fn set_hp_with_mods(&mut self, hp_with_mods: Option) { + self.args.hp_with_mods = hp_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_od(&mut self, od: Option) { + self.args.od = od; + } + + #[wasm_bindgen(setter = odWithMods)] + pub fn set_od_with_mods(&mut self, od_with_mods: Option) { + self.args.od_with_mods = od_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_mode(&mut self, mode: Option) { + self.args.mode = mode; + } + + #[wasm_bindgen(setter = isConvert)] + pub fn set_is_convert(&mut self, is_convert: Option) { + self.args.is_convert = is_convert.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_map(&mut self, map: Option) -> JsResult<()> { + self.args.map = map + .as_deref() + .map(JsDeserializer::from_ref) + .map(JsBeatmap::deserialize) + .transpose()?; + + Ok(()) } } @@ -71,41 +150,3 @@ impl From for JsBeatmapAttributes { } } } - -impl TryFrom for BeatmapAttributesBuilder { - type Error = JsError; - - fn try_from(args: BeatmapAttributesArgs) -> Result { - let mut builder = Self::new(); - - if let Some(map) = args.map { - builder = builder.map(&map.inner); - } - - if let Some(mode) = args.mode { - builder = builder.mode(mode.into(), args.is_convert); - } - - if let Some(clock_rate) = args.clock_rate { - builder = builder.clock_rate(clock_rate); - } - - if let Some(ar) = args.ar { - builder = builder.ar(ar, args.ar_with_mods); - } - - if let Some(cs) = args.cs { - builder = builder.cs(cs, args.cs_with_mods); - } - - if let Some(hp) = args.hp { - builder = builder.hp(hp, args.hp_with_mods); - } - - if let Some(od) = args.od { - builder = builder.od(od, args.od_with_mods); - } - - Ok(builder.mods(args.mods)) - } -} diff --git a/src/difficulty.rs b/src/difficulty.rs index 03b9fc4..73f6729 100644 --- a/src/difficulty.rs +++ b/src/difficulty.rs @@ -1,4 +1,3 @@ -use rosu_pp::Difficulty; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ @@ -14,7 +13,7 @@ use crate::{ #[wasm_bindgen(js_name = Difficulty)] #[derive(Clone)] pub struct JsDifficulty { - pub(crate) inner: Difficulty, + pub(crate) args: DifficultyArgs, } #[wasm_bindgen(js_class = Difficulty)] @@ -25,18 +24,18 @@ impl JsDifficulty { #[cfg(feature = "panic_hook")] console_error_panic_hook::set_once(); - let inner = if let Some(ref args) = args { - util::from_value::(args)?.as_difficulty() - } else { - Difficulty::new() - }; + let args = args + .as_deref() + .map(util::from_value::) + .transpose()? + .unwrap_or_default(); - Ok(Self { inner }) + Ok(Self { args }) } /// Perform the difficulty calculation. pub fn calculate(&self, map: &JsBeatmap) -> JsDifficultyAttributes { - JsDifficultyAttributes::from(self.inner.calculate(&map.inner)) + JsDifficultyAttributes::from(self.args.as_difficulty().calculate(&map.inner)) } /// Perform the difficulty calculation but instead of evaluating strain @@ -44,7 +43,7 @@ impl JsDifficulty { /// /// Suitable to plot the difficulty over time. pub fn strains(&self, map: &JsBeatmap) -> JsStrains { - self.inner.strains(&map.inner).into() + self.args.as_difficulty().strains(&map.inner).into() } /// Returns a gradual difficulty calculator for the current difficulty settings. @@ -58,4 +57,64 @@ impl JsDifficulty { pub fn gradual_performance(&self, map: &JsBeatmap) -> JsGradualPerformance { JsGradualPerformance::new(self, map) } + + #[wasm_bindgen(setter)] + pub fn set_mods(&mut self, mods: Option) { + self.args.mods = mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter = clockRate)] + pub fn set_clock_rate(&mut self, clock_rate: Option) { + self.args.clock_rate = clock_rate; + } + + #[wasm_bindgen(setter)] + pub fn set_ar(&mut self, ar: Option) { + self.args.ar = ar; + } + + #[wasm_bindgen(setter = arWithMods)] + pub fn set_ar_with_mods(&mut self, ar_with_mods: Option) { + self.args.ar_with_mods = ar_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_cs(&mut self, cs: Option) { + self.args.cs = cs; + } + + #[wasm_bindgen(setter = csWithMods)] + pub fn set_cs_with_mods(&mut self, cs_with_mods: Option) { + self.args.cs_with_mods = cs_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_hp(&mut self, hp: Option) { + self.args.hp = hp; + } + + #[wasm_bindgen(setter = hpWithMods)] + pub fn set_hp_with_mods(&mut self, hp_with_mods: Option) { + self.args.hp_with_mods = hp_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_od(&mut self, od: Option) { + self.args.od = od; + } + + #[wasm_bindgen(setter = odWithMods)] + pub fn set_od_with_mods(&mut self, od_with_mods: Option) { + self.args.od_with_mods = od_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter = passedObjects)] + pub fn set_passed_objects(&mut self, passed_objects: Option) { + self.args.passed_objects = passed_objects; + } + + #[wasm_bindgen(setter = hardrockOffsets)] + pub fn set_hardrock_offsets(&mut self, hardrock_offsets: Option) { + self.args.hardrock_offsets = hardrock_offsets; + } } diff --git a/src/gradual/difficulty.rs b/src/gradual/difficulty.rs index 4436dd2..e0bf9f6 100644 --- a/src/gradual/difficulty.rs +++ b/src/gradual/difficulty.rs @@ -16,7 +16,7 @@ impl JsGradualDifficulty { #[wasm_bindgen(constructor)] pub fn new(difficulty: &JsDifficulty, map: &JsBeatmap) -> JsGradualDifficulty { Self { - inner: GradualDifficulty::new(difficulty.inner.clone(), &map.inner), + inner: GradualDifficulty::new(difficulty.args.as_difficulty(), &map.inner), } } diff --git a/src/gradual/performance.rs b/src/gradual/performance.rs index 35ef4ee..56abdf6 100644 --- a/src/gradual/performance.rs +++ b/src/gradual/performance.rs @@ -18,7 +18,7 @@ impl JsGradualPerformance { #[wasm_bindgen(constructor)] pub fn new(difficulty: &JsDifficulty, map: &JsBeatmap) -> JsGradualPerformance { Self { - inner: GradualPerformance::new(difficulty.inner.clone(), &map.inner), + inner: GradualPerformance::new(difficulty.args.as_difficulty(), &map.inner), } } diff --git a/src/performance.rs b/src/performance.rs index 52f0917..531511c 100644 --- a/src/performance.rs +++ b/src/performance.rs @@ -2,7 +2,9 @@ use rosu_pp::Performance; use wasm_bindgen::prelude::wasm_bindgen; use crate::{ - args::performance::{JsMapOrAttributes, JsPerformanceArgs, MapOrAttrs, PerformanceArgs}, + args::performance::{ + JsHitResultPriority, JsMapOrAttributes, JsPerformanceArgs, MapOrAttrs, PerformanceArgs, + }, attributes::performance::JsPerformanceAttributes, util, JsResult, }; @@ -59,4 +61,109 @@ impl JsPerformance { Ok(attrs) } + + #[wasm_bindgen(setter)] + pub fn set_mods(&mut self, mods: Option) { + self.args.mods = mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter = clockRate)] + pub fn set_clock_rate(&mut self, clock_rate: Option) { + self.args.clock_rate = clock_rate; + } + + #[wasm_bindgen(setter)] + pub fn set_ar(&mut self, ar: Option) { + self.args.ar = ar; + } + + #[wasm_bindgen(setter = arWithMods)] + pub fn set_ar_with_mods(&mut self, ar_with_mods: Option) { + self.args.ar_with_mods = ar_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_cs(&mut self, cs: Option) { + self.args.cs = cs; + } + + #[wasm_bindgen(setter = csWithMods)] + pub fn set_cs_with_mods(&mut self, cs_with_mods: Option) { + self.args.cs_with_mods = cs_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_hp(&mut self, hp: Option) { + self.args.hp = hp; + } + + #[wasm_bindgen(setter = hpWithMods)] + pub fn set_hp_with_mods(&mut self, hp_with_mods: Option) { + self.args.hp_with_mods = hp_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter)] + pub fn set_od(&mut self, od: Option) { + self.args.od = od; + } + + #[wasm_bindgen(setter = odWithMods)] + pub fn set_od_with_mods(&mut self, od_with_mods: Option) { + self.args.od_with_mods = od_with_mods.unwrap_or_default(); + } + + #[wasm_bindgen(setter = passedObjects)] + pub fn set_passed_objects(&mut self, passed_objects: Option) { + self.args.passed_objects = passed_objects; + } + + #[wasm_bindgen(setter = hardrockOffsets)] + pub fn set_hardrock_offsets(&mut self, hardrock_offsets: Option) { + self.args.hardrock_offsets = hardrock_offsets; + } + + #[wasm_bindgen(setter)] + pub fn set_accuracy(&mut self, accuracy: Option) { + self.args.accuracy = accuracy; + } + + #[wasm_bindgen(setter)] + pub fn set_combo(&mut self, combo: Option) { + self.args.combo = combo; + } + + #[wasm_bindgen(setter = nGeki)] + pub fn set_n_geki(&mut self, n_geki: Option) { + self.args.n_geki = n_geki; + } + + #[wasm_bindgen(setter = nKatu)] + pub fn set_n_katu(&mut self, n_katu: Option) { + self.args.n_katu = n_katu; + } + + #[wasm_bindgen(setter)] + pub fn set_n300(&mut self, n300: Option) { + self.args.n300 = n300; + } + + #[wasm_bindgen(setter)] + pub fn set_n100(&mut self, n100: Option) { + self.args.n100 = n100; + } + + #[wasm_bindgen(setter)] + pub fn set_n50(&mut self, n50: Option) { + self.args.n50 = n50; + } + + #[wasm_bindgen(setter)] + pub fn set_misses(&mut self, misses: Option) { + self.args.misses = misses; + } + + #[wasm_bindgen(setter = hitresultPriority)] + pub fn set_hitresult_priority(&mut self, hitresult_priority: Option) { + self.args.hitresult_priority = hitresult_priority.map_or_else(Default::default, From::from); + } }