diff --git a/Cargo.lock b/Cargo.lock index d2b6af6f..b42634e9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1107,7 +1107,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -2915,7 +2915,7 @@ dependencies = [ [[package]] name = "rosu-pp" version = "3.1.0" -source = "git+https://github.com/MaxOhn/rosu-pp?branch=pp-update#f5821786819bb9849ca0e0bdff669c1a1ea1fbd0" +source = "git+https://github.com/MaxOhn/rosu-pp?branch=pp-update#66cfb18f575ee318f053d035267203a121e25802" dependencies = [ "divan", "rosu-map", @@ -2925,7 +2925,7 @@ dependencies = [ [[package]] name = "rosu-pp-older" version = "0.2.0" -source = "git+https://github.com/MaxOhn/rosu-pp-older.git?branch=main#ae01e30d7e80e8e3ea6cf84bad79b6172a8407f3" +source = "git+https://github.com/MaxOhn/rosu-pp-older.git?branch=main#d1d0186958f66b9d41dfb86d04ffef586d3d4480" dependencies = [ "rosu-map", "rosu-pp", @@ -3034,7 +3034,7 @@ dependencies = [ "errno", "libc", "linux-raw-sys", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] diff --git a/bathbot/src/active/impls/leaderboard.rs b/bathbot/src/active/impls/leaderboard.rs index c1567b4c..a44c1bef 100644 --- a/bathbot/src/active/impls/leaderboard.rs +++ b/bathbot/src/active/impls/leaderboard.rs @@ -54,7 +54,7 @@ impl IActiveMessage for LeaderboardPagination { let _ = write!( author_text, "[{}K] ", - self.map.attributes().build().cs as u32 + self.map.attributes().build().cs() as u32 ); } diff --git a/bathbot/src/active/impls/map.rs b/bathbot/src/active/impls/map.rs index d24e2aff..c7552a5a 100644 --- a/bathbot/src/active/impls/map.rs +++ b/bathbot/src/active/impls/map.rs @@ -89,11 +89,8 @@ impl IActiveMessage for MapPagination { let clock_rate = match (self.attrs.bpm, self.attrs.clock_rate) { (None, None) => self.mods.legacy_clock_rate(), - (_, Some(clock_rate)) => clock_rate, - (Some(new_bpm), None) => { - let old_bpm = map.bpm; - (new_bpm / old_bpm) as f64 - } + (_, Some(clock_rate)) => clock_rate.clamp(0.01, 100.0), + (Some(new_bpm), None) => (new_bpm / map.bpm).clamp(0.01, 100.0) as f64, }; seconds_total = (seconds_total as f64 / clock_rate) as u32; @@ -130,7 +127,8 @@ impl IActiveMessage for MapPagination { .attributes() .mods(&self.mods) .clock_rate(clock_rate) - .build(); + .build() + .apply_clock_rate(); const ACCS: [f32; 4] = [95.0, 97.0, 99.0, 100.0]; let mut pps = Vec::with_capacity(ACCS.len()); @@ -221,10 +219,10 @@ impl IActiveMessage for MapPagination { "BPM: `{}` Objects: `{}`\nCS: `{}` AR: `{}` OD: `{}` HP: `{}` Spinners: `{}`", round(bpm as f32), map.count_circles + map.count_sliders + map.count_spinners, - round(map_attrs.cs as f32), + round(map_attrs.cs), round(map_attrs.ar as f32), round(map_attrs.od as f32), - round(map_attrs.hp as f32), + round(map_attrs.hp), map.count_spinners, ); diff --git a/bathbot/src/active/impls/profile/top100_stats.rs b/bathbot/src/active/impls/profile/top100_stats.rs index 0c43f610..621b874f 100644 --- a/bathbot/src/active/impls/profile/top100_stats.rs +++ b/bathbot/src/active/impls/profile/top100_stats.rs @@ -12,8 +12,8 @@ pub(super) struct Top100Stats { pub pp: MinMaxAvg, pub stars: MinMaxAvg, pub ar: MinMaxAvg, - pub cs: MinMaxAvg, - pub hp: MinMaxAvg, + pub cs: MinMaxAvg, + pub hp: MinMaxAvg, pub od: MinMaxAvg, pub bpm: MinMaxAvg, pub len: MinMaxAvg, @@ -98,14 +98,15 @@ impl Top100Stats { this.pp.add(pp); let map_attrs = map.attributes().mods(score.mods.clone()).build(); - - this.ar.add(map_attrs.ar); - this.cs.add(map_attrs.cs); - this.hp.add(map_attrs.hp); - this.od.add(map_attrs.od); - this.bpm.add(map.bpm() * map_attrs.clock_rate as f32); - this.len - .add(map.seconds_drain() as f32 / map_attrs.clock_rate as f32); + let clock_rate = map_attrs.clock_rate() as f32; + let adjusted_map_attrs = map_attrs.apply_clock_rate(); + + this.ar.add(adjusted_map_attrs.ar); + this.cs.add(adjusted_map_attrs.cs); + this.hp.add(adjusted_map_attrs.hp); + this.od.add(adjusted_map_attrs.od); + this.bpm.add(map.bpm() * clock_rate); + this.len.add(map.seconds_drain() as f32 / clock_rate); } Ok(this) diff --git a/bathbot/src/active/impls/recent_list.rs b/bathbot/src/active/impls/recent_list.rs index fc631272..a9beee11 100644 --- a/bathbot/src/active/impls/recent_list.rs +++ b/bathbot/src/active/impls/recent_list.rs @@ -76,7 +76,7 @@ impl IActiveMessage for RecentListPagination { let _ = write!( description, "\t{}", - KeyFormatter::new(&score.mods, map.attributes().build().cs as f32) + KeyFormatter::new(&score.mods, map.attributes().build().cs()) ); } diff --git a/bathbot/src/active/impls/simulate/data.rs b/bathbot/src/active/impls/simulate/data.rs index 1ee008e4..4a0ab6f3 100644 --- a/bathbot/src/active/impls/simulate/data.rs +++ b/bathbot/src/active/impls/simulate/data.rs @@ -39,10 +39,12 @@ impl SimulateData { let mod_bits = mods.bits(); - if let Some(new_bpm) = self.bpm.filter(|_| self.clock_rate.is_none()) { + if let Some(ref mut clock_rate) = self.clock_rate { + *clock_rate = clock_rate.clamp(0.01, 100.0) + } else if let Some(new_bpm) = self.bpm { let old_bpm = map.bpm(); - self.clock_rate = Some((new_bpm / old_bpm) as f64); + self.clock_rate = Some((new_bpm / old_bpm).clamp(0.01, 100.0) as f64); } macro_rules! simulate { diff --git a/bathbot/src/active/impls/simulate/mod.rs b/bathbot/src/active/impls/simulate/mod.rs index d140ff0c..1541e020 100644 --- a/bathbot/src/active/impls/simulate/mod.rs +++ b/bathbot/src/active/impls/simulate/mod.rs @@ -868,8 +868,7 @@ impl SimulateMap { } let attrs = builder.mods(bits).build(); - - let clock_rate = attrs.clock_rate; + let clock_rate = attrs.clock_rate(); let start_time = map.hit_objects.first().map_or(0.0, |h| h.start_time); let end_time = map.hit_objects.last().map_or(0.0, |h| match &h.kind { @@ -893,20 +892,22 @@ impl SimulateMap { } let (cs_key, cs_value) = if map.mode == Mode::Mania { - ("Keys", MapInfo::keys(bits, attrs.cs as f32)) + ("Keys", MapInfo::keys(bits, attrs.cs())) } else { - ("CS", round(attrs.cs as f32)) + ("CS", round(attrs.cs())) }; + let adjusted_attrs = attrs.apply_clock_rate(); + format!( "Length: `{len}` BPM: `{bpm}` Objects: `{objs}`\n\ {cs_key}: `{cs_value}` AR: `{ar}` OD: `{od}` HP: `{hp}` Stars: `{stars}`", len = SecToMinSec::new(sec_drain), bpm = round(bpm), objs = self.n_objects(), - ar = round(attrs.ar as f32), - od = round(attrs.od as f32), - hp = round(attrs.hp as f32), + ar = round(adjusted_attrs.ar as f32), + od = round(adjusted_attrs.od as f32), + hp = round(adjusted_attrs.hp), stars = round(stars), ) } diff --git a/bathbot/src/active/impls/single_score.rs b/bathbot/src/active/impls/single_score.rs index 028545b4..7ba16e64 100644 --- a/bathbot/src/active/impls/single_score.rs +++ b/bathbot/src/active/impls/single_score.rs @@ -19,7 +19,7 @@ use bathbot_util::{ numbers::round, }; use eyre::{Report, Result}; -use rosu_pp::model::beatmap::BeatmapAttributes; +use rosu_pp::model::beatmap::{AdjustedBeatmapAttributes, BeatmapAttributes}; use rosu_render::{ClientError as OrdrError, client::error::ApiError as OrdrApiError}; use rosu_v2::{ error::OsuError, @@ -615,11 +615,13 @@ fn apply_settings( writer.push('*'); } + let adjusted_map_attrs = map_attrs.apply_clock_rate(); + let fmt = match curr.inner { - Value::Ar => MapAttribute::AR.fmt(data, &map_attrs), - Value::Cs => MapAttribute::CS.fmt(data, &map_attrs), - Value::Hp => MapAttribute::HP.fmt(data, &map_attrs), - Value::Od => MapAttribute::OD.fmt(data, &map_attrs), + Value::Ar => MapAttribute::AR.fmt(data, &adjusted_map_attrs), + Value::Cs => MapAttribute::CS.fmt(data, &adjusted_map_attrs), + Value::Hp => MapAttribute::HP.fmt(data, &adjusted_map_attrs), + Value::Od => MapAttribute::OD.fmt(data, &adjusted_map_attrs), _ => unreachable!(), }; @@ -642,11 +644,13 @@ fn apply_settings( writer.push('*'); } + let adjusted_map_attrs = map_attrs.apply_clock_rate(); + let fmt = match curr.inner { - Value::Ar => MapAttribute::AR.fmt(data, &map_attrs), - Value::Cs => MapAttribute::CS.fmt(data, &map_attrs), - Value::Hp => MapAttribute::HP.fmt(data, &map_attrs), - Value::Od => MapAttribute::OD.fmt(data, &map_attrs), + Value::Ar => MapAttribute::AR.fmt(data, &adjusted_map_attrs), + Value::Cs => MapAttribute::CS.fmt(data, &adjusted_map_attrs), + Value::Hp => MapAttribute::HP.fmt(data, &adjusted_map_attrs), + Value::Od => MapAttribute::OD.fmt(data, &adjusted_map_attrs), _ => unreachable!(), }; @@ -690,11 +694,13 @@ fn apply_settings( writer.push('*'); } + let adjusted_map_attrs = map_attrs.apply_clock_rate(); + let fmt = match curr.inner { - Value::Ar => MapAttribute::AR.fmt(data, &map_attrs), - Value::Cs => MapAttribute::CS.fmt(data, &map_attrs), - Value::Hp => MapAttribute::HP.fmt(data, &map_attrs), - Value::Od => MapAttribute::OD.fmt(data, &map_attrs), + Value::Ar => MapAttribute::AR.fmt(data, &adjusted_map_attrs), + Value::Cs => MapAttribute::CS.fmt(data, &adjusted_map_attrs), + Value::Hp => MapAttribute::HP.fmt(data, &adjusted_map_attrs), + Value::Od => MapAttribute::OD.fmt(data, &adjusted_map_attrs), _ => unreachable!(), }; @@ -977,7 +983,7 @@ fn write_value( let _ = write!(writer, "{}★", round(data.stars)); } Value::Length => { - let clock_rate = map_attrs.clock_rate as f32; + let clock_rate = map_attrs.clock_rate() as f32; let seconds_drain = (data.map.seconds_drain() as f32 / clock_rate) as u32; if value.y < SettingValue::FOOTER_Y { @@ -995,11 +1001,13 @@ fn write_value( writer.push('`'); } + let adjusted_map_attrs = map_attrs.apply_clock_rate(); + let fmt = match &value.inner { - Value::Ar => MapAttribute::AR.fmt(data, map_attrs), - Value::Cs => MapAttribute::CS.fmt(data, map_attrs), - Value::Hp => MapAttribute::HP.fmt(data, map_attrs), - Value::Od => MapAttribute::OD.fmt(data, map_attrs), + Value::Ar => MapAttribute::AR.fmt(data, &adjusted_map_attrs), + Value::Cs => MapAttribute::CS.fmt(data, &adjusted_map_attrs), + Value::Hp => MapAttribute::HP.fmt(data, &adjusted_map_attrs), + Value::Od => MapAttribute::OD.fmt(data, &adjusted_map_attrs), _ => unreachable!(), }; @@ -1010,7 +1018,7 @@ fn write_value( } } Value::Bpm(emote_text) => { - let clock_rate = map_attrs.clock_rate as f32; + let clock_rate = map_attrs.clock_rate() as f32; let bpm = round(data.map.bpm() * clock_rate); if value.y < SettingValue::FOOTER_Y { @@ -1153,7 +1161,14 @@ impl Display for MapAttributeFormatter<'_> { }) .collect(); - let map_attrs = self.data.map.attributes().mods(alt_mods).build(); + let map_attrs = self + .data + .map + .attributes() + .mods(alt_mods) + .build() + .apply_clock_rate(); + let alt_value = self.map_attr.get_value(&map_attrs); let symbol = match self.value.partial_cmp(&alt_value) { @@ -1178,16 +1193,16 @@ impl MapAttribute { fn fmt<'a>( self, data: &'a ScoreEmbedData, - attrs: &BeatmapAttributes, + attrs: &AdjustedBeatmapAttributes, ) -> MapAttributeFormatter<'a> { MapAttributeFormatter::new(data, self, self.get_value(attrs)) } - fn get_value(self, attrs: &BeatmapAttributes) -> f64 { + fn get_value(self, attrs: &AdjustedBeatmapAttributes) -> f64 { match self { MapAttribute::AR => attrs.ar, - MapAttribute::CS => attrs.cs, - MapAttribute::HP => attrs.hp, + MapAttribute::CS => f64::from(attrs.cs), + MapAttribute::HP => f64::from(attrs.hp), MapAttribute::OD => attrs.od, } } diff --git a/bathbot/src/commands/osu/top/if_.rs b/bathbot/src/commands/osu/top/if_.rs index f22ac204..91d276da 100644 --- a/bathbot/src/commands/osu/top/if_.rs +++ b/bathbot/src/commands/osu/top/if_.rs @@ -386,11 +386,12 @@ impl<'q> Searchable> for TopIfEntry { } let attrs = self.map.attributes().mods(self.score.mods.clone()).build(); + let adjusted_attrs = attrs.apply_clock_rate(); - matches &= criteria.ar.contains(attrs.ar as f32); - matches &= criteria.cs.contains(attrs.cs as f32); - matches &= criteria.hp.contains(attrs.hp as f32); - matches &= criteria.od.contains(attrs.od as f32); + matches &= criteria.ar.contains(adjusted_attrs.ar as f32); + matches &= criteria.cs.contains(adjusted_attrs.cs); + matches &= criteria.hp.contains(adjusted_attrs.hp); + matches &= criteria.od.contains(adjusted_attrs.od as f32); let keys = [ (GameModIntermode::OneKey, 1.0), @@ -406,7 +407,7 @@ impl<'q> Searchable> for TopIfEntry { ] .into_iter() .find_map(|(gamemod, keys)| self.score.mods.contains_intermode(gamemod).then_some(keys)) - .unwrap_or(attrs.cs as f32); + .unwrap_or(adjusted_attrs.cs); matches &= self.map.mode() != GameMode::Mania || criteria.keys.contains(keys); @@ -422,7 +423,7 @@ impl<'q> Searchable> for TopIfEntry { return matches; } - let clock_rate = attrs.clock_rate as f32; + let clock_rate = attrs.clock_rate() as f32; matches &= criteria .length .contains(self.map.seconds_drain() as f32 / clock_rate); diff --git a/bathbot/src/commands/utility/embed_builder.rs b/bathbot/src/commands/utility/embed_builder.rs index 0b682801..d2909080 100644 --- a/bathbot/src/commands/utility/embed_builder.rs +++ b/bathbot/src/commands/utility/embed_builder.rs @@ -9,7 +9,7 @@ use bathbot_util::{ query::{FilterCriteria, Searchable, TopCriteria}, }; use eyre::{Report, Result}; -use rosu_pp::model::beatmap::BeatmapAttributes; +use rosu_pp::model::beatmap::AdjustedBeatmapAttributes; use rosu_v2::{ model::{GameMode, Grade}, prelude::{GameModIntermode, GameMods, RankStatus, Score, ScoreStatistics}, @@ -565,8 +565,12 @@ impl ScoreEmbedDataHalf { } } - fn map_attrs(&self) -> BeatmapAttributes { - self.map.attributes().mods(self.score.mods.clone()).build() + fn map_attrs(&self) -> AdjustedBeatmapAttributes { + self.map + .attributes() + .mods(self.score.mods.clone()) + .build() + .apply_clock_rate() } pub fn ar(&self) -> f64 { @@ -574,11 +578,11 @@ impl ScoreEmbedDataHalf { } pub fn cs(&self) -> f64 { - self.map_attrs().cs + f64::from(self.map_attrs().cs) } pub fn hp(&self) -> f64 { - self.map_attrs().hp + f64::from(self.map_attrs().hp) } pub fn od(&self) -> f64 { @@ -1052,11 +1056,12 @@ impl<'q> Searchable> for ScoreEmbedDataHalf { } let attrs = self.map.attributes().mods(self.score.mods.clone()).build(); + let adjusted_attrs = attrs.apply_clock_rate(); - matches &= criteria.ar.contains(attrs.ar as f32); - matches &= criteria.cs.contains(attrs.cs as f32); - matches &= criteria.hp.contains(attrs.hp as f32); - matches &= criteria.od.contains(attrs.od as f32); + matches &= criteria.ar.contains(adjusted_attrs.ar as f32); + matches &= criteria.cs.contains(adjusted_attrs.cs); + matches &= criteria.hp.contains(adjusted_attrs.hp); + matches &= criteria.od.contains(adjusted_attrs.od as f32); let keys = [ (GameModIntermode::OneKey, 1.0), @@ -1072,7 +1077,7 @@ impl<'q> Searchable> for ScoreEmbedDataHalf { ] .into_iter() .find_map(|(gamemod, keys)| self.score.mods.contains_intermode(gamemod).then_some(keys)) - .unwrap_or(attrs.cs as f32); + .unwrap_or(adjusted_attrs.cs); matches &= self.map.mode() != GameMode::Mania || criteria.keys.contains(keys); @@ -1088,7 +1093,7 @@ impl<'q> Searchable> for ScoreEmbedDataHalf { return matches; } - let clock_rate = attrs.clock_rate as f32; + let clock_rate = attrs.clock_rate() as f32; matches &= criteria .length .contains(self.map.seconds_drain() as f32 / clock_rate); diff --git a/bathbot/src/embeds/osu/attributes.rs b/bathbot/src/embeds/osu/attributes.rs index bbbdc33b..4974fd42 100644 --- a/bathbot/src/embeds/osu/attributes.rs +++ b/bathbot/src/embeds/osu/attributes.rs @@ -49,12 +49,14 @@ impl AttributesEmbed { }; let attrs = builder.build(); + let hit_windows = attrs.hit_windows(); + let adjusted_attrs = attrs.apply_clock_rate(); let adjusted = match kind { - AttributeKind::Ar => attrs.ar, - AttributeKind::Cs => attrs.cs, - AttributeKind::Hp => attrs.hp, - AttributeKind::Od => attrs.od, + AttributeKind::Ar => adjusted_attrs.ar, + AttributeKind::Cs => f64::from(adjusted_attrs.cs), + AttributeKind::Hp => f64::from(adjusted_attrs.hp), + AttributeKind::Od => adjusted_attrs.od, }; let mut mods_name = mods.to_string(); @@ -70,8 +72,8 @@ impl AttributesEmbed { }; let ms = match kind { - AttributeKind::Ar => Some(attrs.hit_windows.ar), - AttributeKind::Od => Some(attrs.hit_windows.od_great), + AttributeKind::Ar => Some(hit_windows.ar.unwrap_or(0.0)), + AttributeKind::Od => Some(hit_windows.od_great.unwrap_or(0.0)), AttributeKind::Cs | AttributeKind::Hp => None, }; diff --git a/bathbot/src/util/osu.rs b/bathbot/src/util/osu.rs index 68740d68..1743f607 100644 --- a/bathbot/src/util/osu.rs +++ b/bathbot/src/util/osu.rs @@ -733,7 +733,9 @@ impl Display for MapInfo<'_> { let mods_bits = mods.bits(); let attrs = builder.mods(mods).build(); - let clock_rate = attrs.clock_rate; + let clock_rate = attrs.clock_rate(); + let adjusted_attrs = attrs.apply_clock_rate(); + let mut sec_drain = self.map.seconds_drain(); let mut bpm = self.map.bpm(); @@ -745,9 +747,9 @@ impl Display for MapInfo<'_> { } let (cs_key, cs_value) = if self.map.mode() == GameMode::Mania { - ("Keys", Self::keys(mods_bits, attrs.cs as f32)) + ("Keys", Self::keys(mods_bits, adjusted_attrs.cs)) } else { - ("CS", round(attrs.cs as f32)) + ("CS", round(adjusted_attrs.cs)) }; write!( @@ -757,9 +759,9 @@ impl Display for MapInfo<'_> { len = SecToMinSec::new(sec_drain), bpm = round(bpm), objs = self.map.n_objects(), - ar = round(attrs.ar as f32), - od = round(attrs.od as f32), - hp = round(attrs.hp as f32), + ar = round(adjusted_attrs.ar as f32), + od = round(adjusted_attrs.od as f32), + hp = round(adjusted_attrs.hp), stars = round(self.stars), ) } diff --git a/bathbot/src/util/searchable.rs b/bathbot/src/util/searchable.rs index 24f7775e..57b4bfa7 100644 --- a/bathbot/src/util/searchable.rs +++ b/bathbot/src/util/searchable.rs @@ -83,14 +83,16 @@ impl Searchable> for Score { .mode((map.mode as u8).into(), map.convert) .build(); - let clock_rate = attrs.clock_rate as f32; + let adjusted_attrs = attrs.apply_clock_rate(); + + let clock_rate = attrs.clock_rate() as f32; let len = map.seconds_drain as f32 / clock_rate; matches &= criteria.0.stars.contains(map.stars); - matches &= criteria.0.ar.contains(attrs.ar as f32); - matches &= criteria.0.cs.contains(attrs.cs as f32); - matches &= criteria.0.hp.contains(attrs.hp as f32); - matches &= criteria.0.od.contains(attrs.od as f32); + matches &= criteria.0.ar.contains(adjusted_attrs.ar as f32); + matches &= criteria.0.cs.contains(adjusted_attrs.cs); + matches &= criteria.0.hp.contains(adjusted_attrs.hp); + matches &= criteria.0.od.contains(adjusted_attrs.od as f32); matches &= criteria.0.length.contains(len); matches &= criteria.0.bpm.contains(map.bpm * clock_rate); @@ -129,19 +131,23 @@ impl Searchable> for (&'_ ScoreSlim, &'_ OsuMap) { let mut matches = true; let attrs = map.attributes().mods(score.mods.clone()).build(); + let adjusted_attrs = attrs.apply_clock_rate(); - let clock_rate = attrs.clock_rate as f32; + let clock_rate = attrs.clock_rate() as f32; let len = map.seconds_drain() as f32 / clock_rate; - matches &= criteria.0.ar.contains(attrs.ar as f32); - matches &= criteria.0.cs.contains(attrs.cs as f32); - matches &= criteria.0.hp.contains(attrs.hp as f32); - matches &= criteria.0.od.contains(attrs.od as f32); + matches &= criteria.0.ar.contains(adjusted_attrs.ar as f32); + matches &= criteria.0.cs.contains(adjusted_attrs.cs); + matches &= criteria.0.hp.contains(adjusted_attrs.hp); + matches &= criteria.0.od.contains(adjusted_attrs.od as f32); matches &= criteria.0.length.contains(len); matches &= criteria.0.bpm.contains(map.bpm() * clock_rate); matches &= score.mode != GameMode::Mania - || criteria.0.keys.contains(keys(&score.mods, attrs.cs as f32)); + || criteria + .0 + .keys + .contains(keys(&score.mods, adjusted_attrs.cs)); if matches && criteria.has_search_terms() { let artist = map.artist().cow_to_ascii_lowercase();