From 53f945b7ac83aec1eb6cf9700a2c2d79017ca09f Mon Sep 17 00:00:00 2001 From: "Kao Li Chin (Gao Li Jin)" <107379868+pacowoc@users.noreply.github.com> Date: Fri, 8 May 2026 17:04:29 +0800 Subject: [PATCH] Added "LN Ratio" display in Mania in the place of useless "key count" (#37581) The "Key Count" metric in mania is very useless since you are already expected to play maps with a specific Key Count when you are queueing. This PR inserts the proportion of LNs (Long Notes) in the place of that metric since it is one of the ways players can gudge their skillsets (This idea comes from reddit) Also improved the test suite for other skillsets by making the architecture more minor ruleset friendly Addresses https://github.com/ppy/osu/discussions/37568. --------- Co-authored-by: Dan Balasescu Co-authored-by: Dean Herbert --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 16 +++++ .../api-beatmaps-rankedplay-mania4k.json | 8 +++ .../Visual/RankedPlay/RankedPlayTestScene.cs | 30 ++++++++-- .../RankedPlay/TestSceneDiscardScreen.cs | 4 +- .../RankedPlay/TestSceneOpponentPickScreen.cs | 4 +- .../Visual/RankedPlay/TestScenePickScreen.cs | 60 ++++++++++++++++++- .../RankedPlay/TestSceneRankedPlayCard.cs | 19 ++++-- .../RankedPlay/TestSceneRankedPlayScreen.cs | 6 +- .../Visual/RankedPlay/TestSceneSongPreview.cs | 7 ++- .../Difficulty/RulesetBeatmapAttribute.cs | 5 ++ osu.Game/Rulesets/Ruleset.cs | 6 ++ .../RankedPlayCardContent.AttributeListing.cs | 7 ++- 12 files changed, 149 insertions(+), 23 deletions(-) create mode 100644 osu.Game.Tests/Resources/Requests/api-beatmaps-rankedplay-mania4k.json diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 75b1b236d3..e9f81c3042 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -485,6 +485,22 @@ namespace osu.Game.Rulesets.Mania }; } + public override IEnumerable GetBeatmapAttributesForRankedPlayCard(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods) + { + var attributes = GetBeatmapAttributesForDisplay(beatmapInfo, mods).ToList(); + + // Key count attribute isn't relevant to ranked play (it's decided by the pool). + attributes.RemoveAll(a => a.Acronym == "KC"); + + float holdNoteRatio = beatmapInfo.TotalObjectCount == 0 ? 0 : (float)beatmapInfo.EndTimeObjectCount / beatmapInfo.TotalObjectCount; + attributes.Insert(0, new RulesetBeatmapAttribute("Hold notes", @"HN", holdNoteRatio, holdNoteRatio, 1) + { + ValueFormat = "P0" + }); + + return attributes; + } + public override IRulesetFilterCriteria CreateRulesetFilterCriteria() { return new ManiaFilterCriteria(); diff --git a/osu.Game.Tests/Resources/Requests/api-beatmaps-rankedplay-mania4k.json b/osu.Game.Tests/Resources/Requests/api-beatmaps-rankedplay-mania4k.json new file mode 100644 index 0000000000..9a465ab486 --- /dev/null +++ b/osu.Game.Tests/Resources/Requests/api-beatmaps-rankedplay-mania4k.json @@ -0,0 +1,8 @@ +[ + {"beatmapset_id":2529695,"difficulty_rating":7.61701,"id":5590715,"mode":"mania","status":"ranked","total_length":396,"user_id":13371424,"version":"[4K] Memoriae Effervescentes","accuracy":7.2,"ar":5,"bpm":220,"convert":false,"count_circles":2097,"count_sliders":3556,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8.2,"hit_length":395,"is_scoreable":true,"last_updated":"2026-04-21T08:35:33Z","mode_int":3,"passcount":1485,"playcount":4006,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5590715","checksum":"5b43b30845408f6bf0cc02d19fa475a4","beatmapset":{"anime_cover":false,"artist":"Laur","artist_unicode":"Laur","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/cover.jpg?1776760548","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/cover@2x.jpg?1776760548","card":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/card.jpg?1776760548","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/card@2x.jpg?1776760548","list":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/list.jpg?1776760548","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/list@2x.jpg?1776760548","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/slimcover.jpg?1776760548","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2529695\/covers\/slimcover@2x.jpg?1776760548"},"creator":"Ainer","favourite_count":55,"genre_id":10,"hype":null,"id":2529695,"language_id":5,"nsfw":false,"offset":0,"play_count":4102,"preview_url":"https:\/\/b.ppy.sh\/preview\/2529695.mp3","source":"osu!mania 7K World Cup 2026","spotlight":false,"status":"ranked","title":"SEV-26","title_unicode":"SEV-26","track_id":11695,"user_id":13371424,"video":false,"bpm":180,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-21T08:35:32Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2191856","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-28T09:22:15Z","rating":8.96296,"storyboard":false,"submitted_date":"2026-03-28T00:58:23Z","tags":"featured artist fa mappers' guild mg mpg osu! original electronic instrumental neurofunk hardcore psytrance speedcore tearout dubstep orchestral artcore sev26 sylvatic encephalitis virus grand finals grandfinals gf mwc 2026 world cup mwc2026 mwc7k2026 tb tiebreaker alicia antipole symmatrix- sardines bruh_shen tehfire polytetral hourius naraicat alexdunk lowgraphics sakura006","availability":{"download_disabled":false,"more_information":null},"ratings":[0,3,0,0,0,0,0,0,0,1,23]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,0,0,0,0,0,9,0,9,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,18,18,0,0,0,0,0,0,0,0,9,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,18,9,9,18,0,18,9,0,0,0,0,0],"exit":[0,36,36,45,63,9,9,54,18,63,72,18,9,0,9,9,0,18,27,18,18,45,0,9,9,9,9,27,9,9,9,9,9,0,0,9,0,9,0,0,0,0,0,0,18,0,0,0,0,9,0,9,0,0,0,0,18,18,9,9,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,9,9,0,0,9,18,0,9,0,0,0,0,0,0,0]},"max_combo":9163,"owners":[{"id":13371424,"username":"Ainer"},{"id":17258072,"username":"Alicia"}]}, + {"beatmapset_id":2518293,"difficulty_rating":3.61767,"id":5602450,"mode":"mania","status":"ranked","total_length":282,"user_id":8425052,"version":"[4K] Innocence","accuracy":8,"ar":5,"bpm":140,"convert":false,"count_circles":3618,"count_sliders":0,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":281,"is_scoreable":true,"last_updated":"2026-04-13T00:00:32Z","mode_int":3,"passcount":369,"playcount":1963,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5602450","checksum":"51319d68b9373eb98807ea5a2187d803","beatmapset":{"anime_cover":false,"artist":"rejection","artist_unicode":"rejection","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/cover.jpg?1776038446","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/cover@2x.jpg?1776038446","card":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/card.jpg?1776038446","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/card@2x.jpg?1776038446","list":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/list.jpg?1776038446","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/list@2x.jpg?1776038446","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/slimcover.jpg?1776038446","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2518293\/covers\/slimcover@2x.jpg?1776038446"},"creator":"Stella-","favourite_count":48,"genre_id":10,"hype":null,"id":2518293,"language_id":3,"nsfw":false,"offset":0,"play_count":2031,"preview_url":"https:\/\/b.ppy.sh\/preview\/2518293.mp3","source":"","spotlight":false,"status":"ranked","title":"White Canvas (feat. Aitsuki Nakuru)","title_unicode":"White Canvas (feat. \u85cd\u6708\u306a\u304f\u308b)","track_id":5057,"user_id":8425052,"video":false,"bpm":140,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-13T00:00:32Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2185354","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-21T23:42:39Z","rating":8.66667,"storyboard":false,"submitted_date":"2026-03-06T10:45:47Z","tags":"fa featured artist japanese pop jpop j-pop electronic encore emotional -emotional vocal pop 02- 2 megarex mrx-074 mrx074 female vocals vocalist m3-2020\u79cb fall m3-46 muse dash the future","availability":{"download_disabled":false,"more_information":null},"ratings":[0,0,0,0,0,1,0,1,0,0,4]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,0,9,0,9,0,0,9,0,0,0,0,0,0,0,0,0,0,0,9,0,9,0,0,0,9,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"exit":[0,0,18,18,9,63,9,27,18,9,18,9,18,9,27,9,36,0,0,9,9,0,9,0,18,27,0,0,0,9,9,0,0,0,0,9,0,9,0,9,9,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,9,0,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"max_combo":3618,"owners":[{"id":8425052,"username":"Stella-"}]}, + {"beatmapset_id":1665948,"difficulty_rating":4.87529,"id":3871759,"mode":"mania","status":"ranked","total_length":379,"user_id":17272017,"version":"[4K] Time Freeze Illusion","accuracy":8.5,"ar":5,"bpm":200,"convert":false,"count_circles":5264,"count_sliders":629,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":377,"is_scoreable":true,"last_updated":"2026-04-13T09:52:53Z","mode_int":3,"passcount":308,"playcount":1738,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/3871759","checksum":"34b98351e32ca3db24ea9fc0055a923b","beatmapset":{"anime_cover":true,"artist":"Release Hallucination","artist_unicode":"Release Hallucination","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/cover.jpg?1776073987","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/cover@2x.jpg?1776073987","card":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/card.jpg?1776073987","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/card@2x.jpg?1776073987","list":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/list.jpg?1776073987","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/list@2x.jpg?1776073987","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/slimcover.jpg?1776073987","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/1665948\/covers\/slimcover@2x.jpg?1776073987"},"creator":"Lazurent","favourite_count":51,"genre_id":11,"hype":null,"id":1665948,"language_id":3,"nsfw":false,"offset":0,"play_count":4811,"preview_url":"https:\/\/b.ppy.sh\/preview\/1665948.mp3","source":"","spotlight":false,"status":"ranked","title":"Chronostasis","title_unicode":"Chronostasis","track_id":4954,"user_id":17272017,"video":false,"bpm":200,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-13T09:52:53Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/1494910","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-20T11:25:45Z","rating":8.33333,"storyboard":false,"submitted_date":"2022-01-03T14:00:15Z","tags":"m3-38 symphonic progressive metal marathon emi gothic kaorin kaoru hirato japanese \u30af\u30ed\u30ce\u30b9\u30bf\u30b7\u30b9 fa featured artist mg mpg mappers' guild l43yrnt","availability":{"download_disabled":false,"more_information":null},"ratings":[0,1,0,0,0,0,0,0,0,1,4]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,0,9,9,63,18,45,18,45,27,0,0,0,0,0,9,0,0,0,0,9,18,0,0,0,9,9,0,0,0,0,0,18,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0],"exit":[0,0,18,63,63,135,261,216,126,81,81,36,18,27,45,27,27,36,9,9,0,18,18,81,27,18,9,18,18,9,0,0,0,36,45,36,0,0,0,0,0,0,9,9,0,18,0,0,9,0,9,0,9,0,0,9,0,9,9,0,0,9,0,0,0,9,0,0,0,0,0,0,0,0,0,9,9,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,0,0,9,0,0,0,0,9]},"max_combo":6974,"owners":[{"id":17272017,"username":"Lazurent"}]}, + {"beatmapset_id":2535368,"difficulty_rating":5.82018,"id":5606802,"mode":"mania","status":"ranked","total_length":296,"user_id":10085090,"version":"[4K] Desperate Soul","accuracy":8.5,"ar":5,"bpm":240,"convert":false,"count_circles":6288,"count_sliders":0,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":290,"is_scoreable":true,"last_updated":"2026-04-16T18:03:43Z","mode_int":3,"passcount":208,"playcount":1738,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5606802","checksum":"55451c2f43654e4ec7b9bb156b93148e","beatmapset":{"anime_cover":false,"artist":"Imperial Circus Dead Decadence","artist_unicode":"Imperial Circus Dead Decadence","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/cover.jpg?1776362637","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/cover@2x.jpg?1776362637","card":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/card.jpg?1776362637","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/card@2x.jpg?1776362637","list":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/list.jpg?1776362637","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/list@2x.jpg?1776362637","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/slimcover.jpg?1776362637","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2535368\/covers\/slimcover@2x.jpg?1776362637"},"creator":"Carpihat","favourite_count":48,"genre_id":11,"hype":null,"id":2535368,"language_id":3,"nsfw":false,"offset":0,"play_count":1961,"preview_url":"https:\/\/b.ppy.sh\/preview\/2535368.mp3","source":"","spotlight":false,"status":"ranked","title":"Jashin no Konrei, Gi wa Ai to Shiru.","title_unicode":"\u90aa\u795e\u306e\u5a5a\u793c\u3001\u5100\u306f\u611b\u3068\u77e5\u308b\u3002","track_id":862,"user_id":10085090,"video":false,"bpm":240,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-04-16T18:03:42Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2195210","nominations_summary":{"current":2,"eligible_main_rulesets":["mania"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-18T18:05:56Z","rating":10,"storyboard":false,"submitted_date":"2026-04-07T09:36:37Z","tags":"cthulhu wedding blackened melodic symphonic death black metal melodeath icdd \u72c2\u304a\u3057\u304f\u54b2\u3044\u305f\u51c4\u60e8\u306a\u9ab8\u306f\u594f\u3067\u3001\u611b\u304a\u3057\u304f\u88c2\u3044\u305f\u5c11\u5973\u306f\u8056\u9910\u306e\u8a5e\u3092\u8b33\u3046\u3002 kurooshiku saita seisan na mukuro wa kanaderu itooshiku shoujo seisen no kotoba wo utau fa featured artist japanese mpg mg mappers' guild","availability":{"download_disabled":false,"more_information":null},"ratings":[0,0,0,0,0,0,0,0,0,0,1]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,9,27,27,27,9,9,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"exit":[0,0,0,27,63,117,108,36,63,36,9,45,9,27,9,0,9,9,0,36,9,36,0,9,0,9,9,0,0,0,27,9,9,0,0,0,0,0,9,0,9,27,9,0,9,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]},"max_combo":6288,"owners":[{"id":10085090,"username":"Carpihat"}]}, + {"beatmapset_id":2348607,"difficulty_rating":5.57212,"id":5140134,"mode":"mania","status":"ranked","total_length":263,"user_id":23384715,"version":"[4K] Enraged Apparition","accuracy":8,"ar":10,"bpm":250,"convert":false,"count_circles":4682,"count_sliders":120,"count_spinners":0,"cs":4,"deleted_at":null,"drain":8,"hit_length":251,"is_scoreable":true,"last_updated":"2026-03-26T20:54:51Z","mode_int":3,"passcount":404,"playcount":2674,"ranked":1,"url":"https:\/\/osu.ppy.sh\/beatmaps\/5140134","checksum":"a2dd0c1b8aa0d3396b47621b758c6adf","beatmapset":{"anime_cover":true,"artist":"Imperial Circus Dead Decadence","artist_unicode":"Imperial Circus Dead Decadence","covers":{"cover":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/cover.jpg?1774558514","cover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/cover@2x.jpg?1774558514","card":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/card.jpg?1774558514","card@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/card@2x.jpg?1774558514","list":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/list.jpg?1774558514","list@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/list@2x.jpg?1774558514","slimcover":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/slimcover.jpg?1774558514","slimcover@2x":"https:\/\/assets.ppy.sh\/beatmaps\/2348607\/covers\/slimcover@2x.jpg?1774558514"},"creator":"Sayuka","favourite_count":435,"genre_id":11,"hype":null,"id":2348607,"language_id":3,"nsfw":false,"offset":0,"play_count":118018,"preview_url":"https:\/\/b.ppy.sh\/preview\/2348607.mp3","source":"","spotlight":false,"status":"ranked","title":"Shinbatsu o Tadori Kyoukotsu ni Itaru","title_unicode":"\u795e\u7f70\u3092\u8fbf\u308a\u72c2\u9aa8\u306b\u81f3\u308b","track_id":8223,"user_id":11322604,"video":false,"bpm":250,"can_be_hyped":false,"deleted_at":null,"discussion_enabled":true,"discussion_locked":false,"is_scoreable":true,"last_updated":"2026-03-26T20:54:46Z","legacy_thread_url":"https:\/\/osu.ppy.sh\/community\/forums\/topics\/2061550","nominations_summary":{"current":3,"eligible_main_rulesets":["osu"],"required_meta":{"main_ruleset":2,"non_main_ruleset":1}},"ranked":1,"ranked_date":"2026-04-12T17:43:03Z","rating":9.11539,"storyboard":false,"submitted_date":"2025-04-02T11:30:44Z","tags":"maaadbot rhythmnoodles ciiyus icdd sinyus20 oomf chan ciyus miapah fort danilmaz1 mekadon -aly arthro roupus julie maiev worthlessnut9 frawog mithew m1ts shoyeu take amats sprixx chocomilk \u9ec4\u6cc9\u3088\u308a\u8074\u3053\u3086\u3001\u7687\u56fd\u306e\u71c8\u3068\u7114\u306e\u5c11\u5973\u3002 japanese male vocals symphonic melodic black death metal doujin hull kim rib:y(uhki) rib yuhki shuhei js jumpstream hs handstream stream stamina jack chordjack rice fa featured artist","availability":{"download_disabled":false,"more_information":null},"ratings":[0,7,0,0,1,0,1,1,3,10,81]},"current_user_playcount":0,"failtimes":{"fail":[0,0,0,18,72,54,36,36,18,9,9,9,0,0,0,9,63,18,0,0,0,0,0,0,9,0,0,0,9,9,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,18,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0],"exit":[0,0,9,243,90,198,216,126,18,81,63,36,9,0,18,135,45,27,27,18,18,9,0,0,0,0,9,9,18,27,36,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,9,27,18,9,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,0,27,0,0,0,0,0,0,0,0,0,0,0,0,9,0,0,0,0,0,0,0,0,9,9,0]},"max_combo":5113,"owners":[{"id":16018038,"username":"frawog"},{"id":23384715,"username":"Worthlessnut9"}]} + +] diff --git a/osu.Game.Tests/Visual/RankedPlay/RankedPlayTestScene.cs b/osu.Game.Tests/Visual/RankedPlay/RankedPlayTestScene.cs index f56b7a7725..67b05e0b6a 100644 --- a/osu.Game.Tests/Visual/RankedPlay/RankedPlayTestScene.cs +++ b/osu.Game.Tests/Visual/RankedPlay/RankedPlayTestScene.cs @@ -9,6 +9,7 @@ using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Online.Rooms; +using osu.Game.Rulesets; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay; using osu.Game.Tests.Resources; using osu.Game.Tests.Visual.Multiplayer; @@ -18,14 +19,26 @@ namespace osu.Game.Tests.Visual.RankedPlay public abstract partial class RankedPlayTestScene : MultiplayerTestScene { /// - /// Returns 5 sample s. + /// Returns 5 sample of the chosen ruleset s. /// - protected static APIBeatmap[] GetSampleBeatmaps() + protected static APIBeatmap[] GetSampleBeatmaps(RulesetInfo ruleset) { - using var resourceStream = TestResources.OpenResource("Requests/api-beatmaps-rankedplay.json"); - using var reader = new StreamReader(resourceStream); + switch (ruleset.OnlineID) + { + case 3: + { + using var resourceStream = TestResources.OpenResource("Requests/api-beatmaps-rankedplay-mania4k.json"); + using var reader = new StreamReader(resourceStream); + return JsonConvert.DeserializeObject(reader.ReadToEnd())!; + } - return JsonConvert.DeserializeObject(reader.ReadToEnd())!; + default: + { + using var resourceStream = TestResources.OpenResource("Requests/api-beatmaps-rankedplay.json"); + using var reader = new StreamReader(resourceStream); + return JsonConvert.DeserializeObject(reader.ReadToEnd())!; + } + } } /// @@ -33,7 +46,12 @@ namespace osu.Game.Tests.Visual.RankedPlay /// public class BeatmapRequestHandler { - public readonly APIBeatmap[] Beatmaps = GetSampleBeatmaps(); + public APIBeatmap[] Beatmaps; + + public BeatmapRequestHandler(RulesetInfo ruleset) + { + Beatmaps = GetSampleBeatmaps(ruleset); + } public bool HandleRequest(APIRequest request) { diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneDiscardScreen.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneDiscardScreen.cs index e01e26524f..72e81dcef7 100644 --- a/osu.Game.Tests/Visual/RankedPlay/TestSceneDiscardScreen.cs +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneDiscardScreen.cs @@ -6,6 +6,7 @@ using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay; namespace osu.Game.Tests.Visual.RankedPlay @@ -26,7 +27,8 @@ namespace osu.Game.Tests.Visual.RankedPlay AddStep("load screen", () => LoadScreen(screen = new RankedPlayScreen(MultiplayerClient.ClientRoom!))); AddUntilStep("screen loaded", () => screen.IsLoaded); - var requestHandler = new BeatmapRequestHandler(); + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new OsuRuleset().RulesetInfo)); AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs index f747004bbd..320a769595 100644 --- a/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs @@ -6,6 +6,7 @@ using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay; namespace osu.Game.Tests.Visual.RankedPlay @@ -26,7 +27,8 @@ namespace osu.Game.Tests.Visual.RankedPlay AddStep("load screen", () => LoadScreen(screen = new RankedPlayScreen(MultiplayerClient.ClientRoom!))); AddUntilStep("screen loaded", () => screen.IsLoaded); - var requestHandler = new BeatmapRequestHandler(); + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new OsuRuleset().RulesetInfo)); AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); diff --git a/osu.Game.Tests/Visual/RankedPlay/TestScenePickScreen.cs b/osu.Game.Tests/Visual/RankedPlay/TestScenePickScreen.cs index ad3b008975..eb5c187f95 100644 --- a/osu.Game.Tests/Visual/RankedPlay/TestScenePickScreen.cs +++ b/osu.Game.Tests/Visual/RankedPlay/TestScenePickScreen.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Linq; +using NUnit.Framework; using osu.Framework.Extensions; using osu.Framework.Testing; using osu.Game.Graphics.UserInterface; @@ -9,6 +10,8 @@ using osu.Game.Online.API; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand; using osuTK.Input; @@ -30,8 +33,63 @@ namespace osu.Game.Tests.Visual.RankedPlay AddStep("load screen", () => LoadScreen(screen = new RankedPlayScreen(MultiplayerClient.ClientRoom!))); AddUntilStep("screen loaded", () => screen.IsLoaded); + } - var requestHandler = new BeatmapRequestHandler(); + [Test] + public void TestOsu() + { + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new OsuRuleset().RulesetInfo)); + + AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); + + AddStep("set pick state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.CardPlay, state => state.ActiveUserId = API.LocalUser.Value.OnlineID).WaitSafely()); + + AddWaitStep("wait some", 5); + + AddStep("reveal cards", () => + { + for (int i = 0; i < 5; i++) + { + int i2 = i; + MultiplayerClient.RankedPlayRevealCard(hand => hand[i2], new MultiplayerPlaylistItem + { + ID = i2, + BeatmapID = requestHandler.Beatmaps[i2].OnlineID + }).WaitSafely(); + } + }); + + for (int i = 0; i < 3; i++) + { + int i2 = i; + AddStep($"click card {i2}", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().ElementAt(i2)); + InputManager.Click(MouseButton.Left); + }); + } + + AddWaitStep("wait", 3); + + AddStep("click play button", () => + { + var button = screen + .ChildrenOfType() + .First(it => it.Selected) + .ChildrenOfType() + .First(); + + InputManager.MoveMouseTo(button); + InputManager.Click(MouseButton.Left); + }); + } + + [Test] + public void TestMania() + { + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new ManiaRuleset().RulesetInfo)); AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayCard.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayCard.cs index 54fc56a4f9..a1a449b703 100644 --- a/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayCard.cs +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayCard.cs @@ -12,6 +12,10 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Taiko; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand; using osuTK; @@ -31,8 +35,6 @@ namespace osu.Game.Tests.Visual.RankedPlay [Cached] private readonly SongPreviewParticleContainer particleContainer; - private readonly BeatmapRequestHandler requestHandler = new BeatmapRequestHandler(); - public TestSceneRankedPlayCard() { base.Content.AddRange(new Drawable[] @@ -121,6 +123,8 @@ namespace osu.Game.Tests.Visual.RankedPlay [Test] public void TestCardHand() { + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new OsuRuleset().RulesetInfo)); AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); AddStep("add cards", () => @@ -143,13 +147,16 @@ namespace osu.Game.Tests.Visual.RankedPlay }); } - [Resolved] - private RulesetStore rulesetStore { get; set; } = null!; - [Test] public void TestRulesets() { - var rulesets = rulesetStore.AvailableRulesets.Where(it => it.OnlineID >= 0); + RulesetInfo[] rulesets = + [ + new OsuRuleset().RulesetInfo, + new TaikoRuleset().RulesetInfo, + new CatchRuleset().RulesetInfo, + new ManiaRuleset().RulesetInfo + ]; foreach (var ruleset in rulesets) { diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayScreen.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayScreen.cs index a9ab138062..7de250327d 100644 --- a/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayScreen.cs +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneRankedPlayScreen.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay; using osu.Game.Online.Rooms; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand; @@ -108,7 +109,7 @@ namespace osu.Game.Tests.Visual.RankedPlay AddStep("load screen", () => LoadScreen(screen = new RankedPlayScreen(MultiplayerClient.ClientRoom!))); - var requestHandler = new BeatmapRequestHandler(); + var requestHandler = new BeatmapRequestHandler(Ruleset.Value); AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); @@ -224,7 +225,8 @@ namespace osu.Game.Tests.Visual.RankedPlay AddStep("load screen", () => LoadScreen(screen = new RankedPlayScreen(MultiplayerClient.ClientRoom!))); - var requestHandler = new BeatmapRequestHandler(); + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new OsuRuleset().RulesetInfo)); AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneSongPreview.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneSongPreview.cs index 363082f668..8ee93ad9a1 100644 --- a/osu.Game.Tests/Visual/RankedPlay/TestSceneSongPreview.cs +++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneSongPreview.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Online.API; +using osu.Game.Rulesets.Osu; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card; using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand; using osuTK; @@ -17,18 +18,18 @@ namespace osu.Game.Tests.Visual.RankedPlay { private readonly Bindable previewEnabled = new BindableBool(true); - private readonly BeatmapRequestHandler requestHandler = new BeatmapRequestHandler(); - public override void SetUpSteps() { base.SetUpSteps(); + BeatmapRequestHandler requestHandler = null!; + AddStep("setup ruleset", () => requestHandler = new BeatmapRequestHandler(new OsuRuleset().RulesetInfo)); + AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest); AddStep("add cards", () => { PlayerHandOfCards handOfCards; - Child = handOfCards = new PlayerHandOfCards { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Rulesets/Difficulty/RulesetBeatmapAttribute.cs b/osu.Game/Rulesets/Difficulty/RulesetBeatmapAttribute.cs index db76974c44..c04c84f678 100644 --- a/osu.Game/Rulesets/Difficulty/RulesetBeatmapAttribute.cs +++ b/osu.Game/Rulesets/Difficulty/RulesetBeatmapAttribute.cs @@ -53,6 +53,11 @@ namespace osu.Game.Rulesets.Difficulty /// public AdditionalMetric[] AdditionalMetrics { get; init; } = []; + /// + /// An optional formatting specifier for the values of this attribute. + /// + public string ValueFormat { get; init; } = string.Empty; + public RulesetBeatmapAttribute(LocalisableString label, string acronym, float originalValue, float adjustedValue, float maxValue) { Label = label; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 49043abce8..02644ca8ce 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -430,6 +430,12 @@ namespace osu.Game.Rulesets yield return new RulesetBeatmapAttribute(SongSelectStrings.HPDrain, @"HP", originalDifficulty.DrainRate, adjustedDifficulty.DrainRate, 10); } + /// + /// Overload of for display on Ranked Cards + /// + public virtual IEnumerable GetBeatmapAttributesForRankedPlayCard(IBeatmapInfo beatmapInfo, IReadOnlyCollection mods) => + GetBeatmapAttributesForDisplay(beatmapInfo, mods); + /// /// Creates ruleset-specific beatmap filter criteria to be used on the song select screen. /// diff --git a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Card/RankedPlayCardContent.AttributeListing.cs b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Card/RankedPlayCardContent.AttributeListing.cs index 70f8d312fd..1fca5b09c0 100644 --- a/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Card/RankedPlayCardContent.AttributeListing.cs +++ b/osu.Game/Screens/OnlinePlay/Matchmaking/RankedPlay/Card/RankedPlayCardContent.AttributeListing.cs @@ -97,8 +97,7 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card }, ] }, - ..ruleset.GetBeatmapAttributesForDisplay(beatmap, []) - .Select(attribute => new AttributeRow(attribute)) + ..ruleset.GetBeatmapAttributesForRankedPlayCard(beatmap, []).Select(attribute => new AttributeRow(attribute)) ] }; } @@ -126,7 +125,9 @@ namespace osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card new OsuSpriteText { RelativePositionAxes = Axes.X, - Text = attribute.AdjustedValue.ToStandardFormattedString(maxDecimalDigits: 1), + Text = string.IsNullOrEmpty(attribute.ValueFormat) + ? attribute.AdjustedValue.ToStandardFormattedString(maxDecimalDigits: 1) + : attribute.AdjustedValue.ToString(attribute.ValueFormat), Font = OsuFont.GetFont(size: 9, weight: FontWeight.SemiBold), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreRight,