mirror of
https://github.com/natsukagami/youmubot.git
synced 2025-04-18 16:28:55 +00:00
Handle cs/od/ar/hp with mods
This commit is contained in:
parent
cbb1e14d93
commit
33f19dbaba
3 changed files with 90 additions and 37 deletions
|
@ -27,20 +27,7 @@ pub fn beatmap_embed<'a>(
|
||||||
} else {
|
} else {
|
||||||
format!(" {}", mods)
|
format!(" {}", mods)
|
||||||
};
|
};
|
||||||
let total_length = if mods.intersects(Mods::DT | Mods::NC) {
|
let diff = b.difficulty.apply_mods(mods);
|
||||||
b.total_length * 2 / 3
|
|
||||||
} else if mods.intersects(Mods::HT) {
|
|
||||||
b.total_length * 4 / 3
|
|
||||||
} else {
|
|
||||||
b.total_length
|
|
||||||
};
|
|
||||||
let drain_length = if mods.intersects(Mods::DT | Mods::NC) {
|
|
||||||
b.drain_length * 2 / 3
|
|
||||||
} else if mods.intersects(Mods::HT) {
|
|
||||||
b.drain_length * 4 / 3
|
|
||||||
} else {
|
|
||||||
b.drain_length
|
|
||||||
};
|
|
||||||
c.title(
|
c.title(
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
.push_bold_safe(&b.artist)
|
.push_bold_safe(&b.artist)
|
||||||
|
@ -67,8 +54,9 @@ pub fn beatmap_embed<'a>(
|
||||||
"{:.2}⭐",
|
"{:.2}⭐",
|
||||||
info.map(|v| v.stars as f64).unwrap_or(b.difficulty.stars)
|
info.map(|v| v.stars as f64).unwrap_or(b.difficulty.stars)
|
||||||
),
|
),
|
||||||
false,
|
true,
|
||||||
)
|
)
|
||||||
|
.fields(Some(("Mods", mods, true)).filter(|_| mods != Mods::NOMOD))
|
||||||
.fields(info.map(|info| {
|
.fields(info.map(|info| {
|
||||||
(
|
(
|
||||||
"Calculated pp",
|
"Calculated pp",
|
||||||
|
@ -79,25 +67,20 @@ pub fn beatmap_embed<'a>(
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
}))
|
}))
|
||||||
.fields(Some(("Mods", mods, false)).filter(|_| mods != Mods::NOMOD))
|
|
||||||
.field(
|
.field(
|
||||||
"Length",
|
"Length",
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
.push_bold_safe(Duration(total_length))
|
.push_bold_safe(Duration(diff.total_length))
|
||||||
.push(" (")
|
.push(" (")
|
||||||
.push_bold_safe(Duration(drain_length))
|
.push_bold_safe(Duration(diff.drain_length))
|
||||||
.push(" drain)")
|
.push(" drain)")
|
||||||
.build(),
|
.build(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
.field("Circle Size", format!("{:.1}", b.difficulty.cs), true)
|
.field("Circle Size", format!("{:.1}", diff.cs), true)
|
||||||
.field("Approach Rate", format!("{:.1}", b.difficulty.ar), true)
|
.field("Approach Rate", format!("{:.1}", diff.ar), true)
|
||||||
.field(
|
.field("Overall Difficulty", format!("{:.1}", diff.od), true)
|
||||||
"Overall Difficulty",
|
.field("HP Drain", format!("{:.1}", diff.hp), true)
|
||||||
format!("{:.1}", b.difficulty.od),
|
|
||||||
true,
|
|
||||||
)
|
|
||||||
.field("HP Drain", format!("{:.1}", b.difficulty.hp), true)
|
|
||||||
.field("BPM", b.bpm.round(), true)
|
.field("BPM", b.bpm.round(), true)
|
||||||
.fields(b.difficulty.max_combo.map(|v| ("Max combo", v, true)))
|
.fields(b.difficulty.max_combo.map(|v| ("Max combo", v, true)))
|
||||||
.field("Mode", format_mode(m, b.mode), true)
|
.field("Mode", format_mode(m, b.mode), true)
|
||||||
|
@ -194,7 +177,7 @@ pub fn beatmapset_embed<'a>(
|
||||||
.field(
|
.field(
|
||||||
"Length",
|
"Length",
|
||||||
MessageBuilder::new()
|
MessageBuilder::new()
|
||||||
.push_bold_safe(Duration(b.total_length))
|
.push_bold_safe(Duration(b.difficulty.total_length))
|
||||||
.build(),
|
.build(),
|
||||||
true,
|
true,
|
||||||
)
|
)
|
||||||
|
@ -240,7 +223,7 @@ pub fn beatmapset_embed<'a>(
|
||||||
.push(", HP")
|
.push(", HP")
|
||||||
.push_bold(format!("{:.1}", b.difficulty.hp))
|
.push_bold(format!("{:.1}", b.difficulty.hp))
|
||||||
.push(", ⌛ ")
|
.push(", ⌛ ")
|
||||||
.push_bold(format!("{}", Duration(b.drain_length)))
|
.push_bold(format!("{}", Duration(b.difficulty.drain_length)))
|
||||||
.build(),
|
.build(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
@ -272,6 +255,7 @@ pub(crate) fn score_embed<'a>(
|
||||||
let top_record = top_record
|
let top_record = top_record
|
||||||
.map(|v| format!("| #{} top record!", v))
|
.map(|v| format!("| #{} top record!", v))
|
||||||
.unwrap_or("".to_owned());
|
.unwrap_or("".to_owned());
|
||||||
|
let diff = b.difficulty.apply_mods(s.mods);
|
||||||
m.author(|f| f.name(&u.username).url(u.link()).icon_url(u.avatar_url()))
|
m.author(|f| f.name(&u.username).url(u.link()).icon_url(u.avatar_url()))
|
||||||
.color(0xffb6c1)
|
.color(0xffb6c1)
|
||||||
.title(format!(
|
.title(format!(
|
||||||
|
@ -313,15 +297,15 @@ pub(crate) fn score_embed<'a>(
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.push("CS")
|
.push("CS")
|
||||||
.push_bold(format!("{:.1}", b.difficulty.cs))
|
.push_bold(format!("{:.1}", diff.cs))
|
||||||
.push(", AR")
|
.push(", AR")
|
||||||
.push_bold(format!("{:.1}", b.difficulty.ar))
|
.push_bold(format!("{:.1}", diff.ar))
|
||||||
.push(", OD")
|
.push(", OD")
|
||||||
.push_bold(format!("{:.1}", b.difficulty.od))
|
.push_bold(format!("{:.1}", diff.od))
|
||||||
.push(", HP")
|
.push(", HP")
|
||||||
.push_bold(format!("{:.1}", b.difficulty.hp))
|
.push_bold(format!("{:.1}", diff.hp))
|
||||||
.push(", ⌛ ")
|
.push(", ⌛ ")
|
||||||
.push_bold(format!("{}", Duration(b.drain_length)))
|
.push_bold(format!("{}", Duration(diff.drain_length)))
|
||||||
.build(),
|
.build(),
|
||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
|
|
|
@ -45,6 +45,77 @@ pub struct Difficulty {
|
||||||
pub count_slider: u64,
|
pub count_slider: u64,
|
||||||
pub count_spinner: u64,
|
pub count_spinner: u64,
|
||||||
pub max_combo: Option<u64>,
|
pub max_combo: Option<u64>,
|
||||||
|
|
||||||
|
pub drain_length: Duration,
|
||||||
|
pub total_length: Duration,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Difficulty {
|
||||||
|
// Difficulty calculation is based on
|
||||||
|
// https://www.reddit.com/r/osugame/comments/6phntt/difficulty_settings_table_with_all_values/
|
||||||
|
|
||||||
|
fn apply_everything_by_ratio(&mut self, rat: f64) {
|
||||||
|
self.cs = (self.cs * rat).min(10.0);
|
||||||
|
self.od = (self.od * rat).min(10.0);
|
||||||
|
self.ar = (self.ar * rat).min(10.0);
|
||||||
|
self.hp = (self.hp * rat).min(10.0);
|
||||||
|
}
|
||||||
|
fn apply_ar_by_time_ratio(&mut self, rat: f64) {
|
||||||
|
// Convert AR to approach time...
|
||||||
|
let approach_time = if self.ar < 5.0 {
|
||||||
|
1800.0 - self.ar * 120.0
|
||||||
|
} else {
|
||||||
|
1200.0 - (self.ar - 5.0) * 150.0
|
||||||
|
};
|
||||||
|
// Update it...
|
||||||
|
let approach_time = approach_time * rat;
|
||||||
|
// Convert it back to AR...
|
||||||
|
self.ar = if approach_time > 1200.0 {
|
||||||
|
(1800.0 - approach_time) / 120.0
|
||||||
|
} else {
|
||||||
|
(1200.0 - approach_time) / 150.0 + 5.0
|
||||||
|
};
|
||||||
|
}
|
||||||
|
fn apply_od_by_time_ratio(&mut self, rat: f64) {
|
||||||
|
// Convert OD to hit timing
|
||||||
|
let hit_timing = 79.0 - self.od * 6.0 + 0.5;
|
||||||
|
// Update it...
|
||||||
|
let hit_timing = hit_timing * rat + 0.5 / rat;
|
||||||
|
// then convert back
|
||||||
|
self.od = (79.0 - (hit_timing - 0.5)) / 6.0;
|
||||||
|
}
|
||||||
|
fn apply_length_by_ratio(&mut self, mul: u32, div: u32) {
|
||||||
|
self.drain_length = self.drain_length * mul / div;
|
||||||
|
self.total_length = self.total_length * mul / div;
|
||||||
|
}
|
||||||
|
/// Apply mods to the given difficulty.
|
||||||
|
/// Note that `stars`, `aim` and `speed` cannot be calculated from this alone.
|
||||||
|
pub fn apply_mods(&self, mods: Mods) -> Difficulty {
|
||||||
|
let mut diff = self.clone();
|
||||||
|
|
||||||
|
// Apply mods one by one
|
||||||
|
if mods.contains(Mods::EZ) {
|
||||||
|
diff.apply_everything_by_ratio(0.5);
|
||||||
|
}
|
||||||
|
if mods.contains(Mods::HT) {
|
||||||
|
diff.apply_ar_by_time_ratio(4.0 / 3.0);
|
||||||
|
diff.apply_od_by_time_ratio(4.0 / 3.0);
|
||||||
|
diff.apply_length_by_ratio(4, 3);
|
||||||
|
}
|
||||||
|
if mods.contains(Mods::HR) {
|
||||||
|
let old_cs = diff.cs;
|
||||||
|
diff.apply_everything_by_ratio(1.4);
|
||||||
|
// CS is changed by 1.3 tho
|
||||||
|
diff.cs = old_cs * 1.3;
|
||||||
|
}
|
||||||
|
if mods.contains(Mods::DT) {
|
||||||
|
diff.apply_ar_by_time_ratio(2.0 / 3.0);
|
||||||
|
diff.apply_od_by_time_ratio(2.0 / 3.0);
|
||||||
|
diff.apply_length_by_ratio(2, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
diff
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
#[derive(Clone, Copy, PartialEq, Eq, Debug, Deserialize, Serialize)]
|
||||||
|
@ -152,8 +223,6 @@ pub struct Beatmap {
|
||||||
pub beatmap_id: u64,
|
pub beatmap_id: u64,
|
||||||
pub difficulty_name: String,
|
pub difficulty_name: String,
|
||||||
pub difficulty: Difficulty,
|
pub difficulty: Difficulty,
|
||||||
pub drain_length: Duration,
|
|
||||||
pub total_length: Duration,
|
|
||||||
pub file_hash: String,
|
pub file_hash: String,
|
||||||
pub mode: Mode,
|
pub mode: Mode,
|
||||||
pub favourite_count: u64,
|
pub favourite_count: u64,
|
||||||
|
|
|
@ -139,9 +139,9 @@ impl TryFrom<raw::Beatmap> for Beatmap {
|
||||||
count_slider: parse_from_str(&raw.count_slider)?,
|
count_slider: parse_from_str(&raw.count_slider)?,
|
||||||
count_spinner: parse_from_str(&raw.count_spinner)?,
|
count_spinner: parse_from_str(&raw.count_spinner)?,
|
||||||
max_combo: raw.max_combo.map(parse_from_str).transpose()?,
|
max_combo: raw.max_combo.map(parse_from_str).transpose()?,
|
||||||
|
drain_length: parse_duration(&raw.hit_length)?,
|
||||||
|
total_length: parse_duration(&raw.total_length)?,
|
||||||
},
|
},
|
||||||
drain_length: parse_duration(&raw.hit_length)?,
|
|
||||||
total_length: parse_duration(&raw.total_length)?,
|
|
||||||
file_hash: raw.file_md5,
|
file_hash: raw.file_md5,
|
||||||
mode: parse_mode(&raw.mode)?,
|
mode: parse_mode(&raw.mode)?,
|
||||||
favourite_count: parse_from_str(&raw.favourite_count)?,
|
favourite_count: parse_from_str(&raw.favourite_count)?,
|
||||||
|
|
Loading…
Add table
Reference in a new issue