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 <smoogipoo@smgi.me>
Co-authored-by: Dean Herbert <pe@ppy.sh>
Kao Li Chin (Gao Li Jin)
·
2026-05-08 18:04:29 +09:00
When editing metadata, it's annoying that the editor returns to compose
mode when switching between difficulties. This fixes that fallacy.
Supersedes and closes https://github.com/ppy/osu/pull/36724.
Fixes a bug that allowed for selecting the same mod twice in multiplayer
if the playlist entry has non-default settings for a required mod.
This happens due to a strict equality mod check in the mod set
compatibility check function, which only considers mods duplicates if
their settings are exactly the same. Replacing with a more lenient
`Type` check fixes this.
Adds a regression test for this behavior
Fixes https://github.com/ppy/osu/issues/37625.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
This PR refactors the report popover to have an optional confirmation
message after completing the request. This was initially meant for
#32584, but then I realized I can first test it out on the user
profiles, so I've implemented it here as well. Can be reviewed commit by
commit.
https://github.com/user-attachments/assets/cd59c560-c824-4a5e-bab6-5ecbf5125af1
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Based off of https://github.com/ppy/osu/pull/37478 with some
improvements such as:
* Simpler `progress` handling (single adjustable value instead of 2)
* 2 less drawable paths (replaced with circlular containers)
* Reworked remaining paths to have as little texture sizes as possible
---------
Co-authored-by: Krzysztof Gutkowski <krzysio.gutkowski@gmail.com>
Co-authored-by: Dean Herbert <pe@ppy.sh>
Resolves#37486
Original error log:
```
2026-04-22 21:47:58 [error]: System.ArgumentOutOfRangeException: Index was out of range. Must be non-negative and less than the size of the collection. (Parameter 'index')
2026-04-22 21:47:58 [error]: at System.Collections.Generic.List`1.get_Item(Int32 index)
2026-04-22 21:47:58 [error]: at osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand.PlayerHandOfCards.moveCardFocus(Int32 direction)
2026-04-22 21:47:58 [error]: at osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand.PlayerHandOfCards.OnKeyDown(KeyDownEvent e)
```
Currently,
```osu.Game\Screens\OnlinePlay\Matchmaking\RankedPlay\Hand\PlayerHandOfCards.cs::moveCardFocus```
does not account for the hand being empty (cards.Count ==0 ), and will
attempt to move the card focus in the given direction regardless, which
causes the above index out of range error.
Added empty hand check to moveCardFocus, and a matching test case to
TestScenePlayerCardHand.cs
New test case before changes, recreating the error:
<img width="986" height="232" alt="image"
src="https://github.com/user-attachments/assets/daa62081-c776-44bd-b0d2-382b2dac7938"
/>
After:
<img width="638" height="223" alt="image"
src="https://github.com/user-attachments/assets/d6dcd8b8-8caf-42e3-9999-93dfe3fb6452"
/>
Thing to make release happen.
Reverts #37453
Reverts #37463
Alternative to #37473
Not that I disagree with any of these but I'm just looking to return to
what works so we can do a release because we're on a clock here for
other reasons.
Test which should work but doesn't, so I'm not adding:
```diff
diff --git a/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs b/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs
index f747004bbd..eb8e360d1e 100644
--- a/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs
+++ b/osu.Game.Tests/Visual/RankedPlay/TestSceneOpponentPickScreen.cs
@@ -1,12 +1,17 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// 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.Online.API;
using osu.Game.Online.Multiplayer;
using osu.Game.Online.Multiplayer.MatchTypes.RankedPlay;
using osu.Game.Online.Rooms;
using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay;
+using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Card;
+using osu.Game.Screens.OnlinePlay.Matchmaking.RankedPlay.Hand;
namespace osu.Game.Tests.Visual.RankedPlay
{
@@ -14,6 +19,8 @@ public partial class TestSceneOpponentPickScreen : RankedPlayTestScene
{
private RankedPlayScreen screen = null!;
+ private readonly BeatmapRequestHandler requestHandler = new BeatmapRequestHandler();
+
public override void SetUpSteps()
{
base.SetUpSteps();
@@ -26,8 +33,6 @@ public override void SetUpSteps()
AddStep("load screen", () => LoadScreen(screen = new RankedPlayScreen(MultiplayerClient.ClientRoom!)));
AddUntilStep("screen loaded", () => screen.IsLoaded);
- var requestHandler = new BeatmapRequestHandler();
-
AddStep("setup request handler", () => ((DummyAPIAccess)API).HandleRequest = requestHandler.HandleRequest);
AddStep("set pick state", () => MultiplayerClient.RankedPlayChangeStage(RankedPlayStage.CardPlay, state => state.ActiveUserId = 2).WaitSafely());
@@ -44,7 +49,11 @@ public override void SetUpSteps()
}).WaitSafely();
}
});
+ }
+ [Test]
+ public void TestBasic()
+ {
AddWaitStep("wait", 15);
AddStep("play beatmap", () => MultiplayerClient.PlayUserCard(2, hand => hand[0]).WaitSafely());
@@ -54,5 +63,29 @@ public override void SetUpSteps()
BeatmapID = requestHandler.Beatmaps[0].OnlineID
}).WaitSafely());
}
+
+ [Test]
+ public void TestPickPreviewPlayedOnOpponentPick()
+ {
+ RankedPlayCard.SongPreviewContainer? originalPreview = null;
+
+ AddStep("hover first card",
+ () => InputManager.MoveMouseTo(this.ChildrenOfType<PlayerHandOfCards>().Single().Cards
+ .First(c => c.Item.PlaylistItem.Value != null && c.Item.PlaylistItem.Value.BeatmapID != requestHandler.Beatmaps[0].OnlineID)));
+ AddUntilStep("preview playing", () => originalPreview = this.ChildrenOfType<RankedPlayCard.SongPreviewContainer>().FirstOrDefault(p => p.IsRunning), () => Is.Not.Null);
+
+ AddStep("play beatmap", () => MultiplayerClient.PlayUserCard(2, hand => hand[0]).WaitSafely());
+ AddStep("reveal card", () => MultiplayerClient.RankedPlayRevealUserCard(2, hand => hand[0], new MultiplayerPlaylistItem
+ {
+ ID = 0,
+ BeatmapID = requestHandler.Beatmaps[0].OnlineID
+ }).WaitSafely());
+
+ AddUntilStep("wait for original preview stopped", () => originalPreview?.IsRunning, () => Is.False);
+
+ AddUntilStep("preview playing is opponent's pick",
+ () => ((RankedPlayCard)this.ChildrenOfType<RankedPlayCard.SongPreviewContainer>().SingleOrDefault(p => p.IsRunning)?.Parent!).Item.PlaylistItem.Value?.BeatmapID,
+ () => Is.EqualTo(requestHandler.Beatmaps[0].OnlineID));
+ }
}
}
```
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
RFC.
See https://github.com/ppy/osu/pull/37453#issuecomment-4289403735 for
why.
Of note:
- To facilitate mutual exclusivity of playback `PlayerHandOfCards`
maintains a bindable pointing at the currently playing song preview.
- Because of how card drawables are passed between multiple parenting
drawables, some of which are and some of which are not
`PlayerHandOfCards` instances, DI fails horribly at working with this
bindable unless it is manually managed. See relevant overrides in
`PlayerHandOfCards`.
- I renamed one of the overloads of `HandOfCards.RemoveCard()` to
`DetachCard()` because I found the fact that there are two overloads of
one method that do WILDLY DIFFERENT THINGS utterly *asinine*. (One
overload scrapes the `RankedPlayCard` out for you to plop elsewhere. One
*drops it on the floor entirely*.)
This took way too long to write.
Attempt at adressing the points made in
https://github.com/ppy/osu/pull/37419#issuecomment-4279123323
- `CardContainer` is now being sorted immediately instead of only doing
it once per frame. Given that its only ever gonna have a handful of
children there wasn't really a need to optimize that that in the first
place.
- `HandOfCards.Cards` now exposes `cardLookup.Values` as an
`IEnumerable` instead of exposing the card container's children
directly.
- `HandOfCard` now exposes `GetCardsInDisplayOrder` which returns a copy
of all cards in display order. Since it's making a copy I made sure this
isn't called on any hot code paths.
- `HandOfCard.Clear` previously didn't clear the `cardLookup`
dictionary. Didn't cause any issues since we're not re-adding cards to
the hand anywhere but not good regardless.
Switched to looping over all cards and calling `RemoveCard` to make sure
changes to the removal logic can't get overlooked there again.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
Suboptimal? Sure. But the primary goal is not to crash. Crashing is a
failure of the game programmer.
Better can be done later.
Remedies/fixes https://github.com/ppy/osu/issues/37421.
Due to the push of the relevant screens being delayed it's possible that
the room goes away between the scheduling of the push and the actual
execution of the push.
This maybe closes https://github.com/ppy/osu/issues/37374 but my hopes
are not high.
Includes some extra cleanups I noticed along the way.
Intends to close https://github.com/ppy/osu/issues/37408.
I have got to say, the way ranked play apparently re-invents screen
sub-stacks *again* in a slightly different way to everything else before
had me *very* confused as to why things I would expect to get called
aren't getting called.
Closes: #37402
Issue was `HandOfCards.CardContainer.Compare` working under the
assumption that the it would never be called with the same child for
both entries, which can happen when doing a `BinarySearch` (called in
`RemoveInternal`).
This lead to `IndexOf` returning a negative value despite the card being
present in the container, and the drawable getting disposed but not
actually removed.
I also included a precautionary `cardContainer.Sort()` call before the
removal as well, seemed to work without that from testing but better not
rely on that.
TLDR: card container child sorting was unstable due to poor assumptions
In some cases `SliderPath.GetPathToProgress` used to compute the whole
path when it can be not needed since it can be already stored inside
`calculatedPath` list.
Also some of these use cases will no longer require additional array
wheen only readonly access is all we need.
Fixes
https://discord.com/channels/188630481301012481/188630652340404224/1493678774540304505
The rating distribution is updated once every 5 minutes, so there are
periods where it may not include the local user's rating. This is simply
a workaround where it's considered if it's bounded by it.
Am a little surprised this is as easy to handle as it appears to be,
even if not the cleanest presentation (it's an edge case).
The panels look up the online APIUser models. Maybe I could do this by
doing the lookups async inside `RankedPlayMatchPanel`, but this will
probably do for now?
- [x] Depends on https://github.com/ppy/osu/pull/37226
- [x] Depends on https://github.com/ppy/osu-server-spectator/pull/464
This adds two new components to the queue screen:
- A listing of the most recently completed matches (global).
- A rank distribution graph.
It looks something like this (fake data / test scene):
<img width="1669" height="1005" alt="image"
src="https://github.com/user-attachments/assets/caa57119-4267-4c6e-9898-2f414de865bf"
/>
It's completely dev-design(TM), but I used Lichess as inspiration for
the graph, and the original design document as inspiration for the
panels.
As for the history, because these are _completed_ matches one of the
player life points will always be 0, but I've designed it so as to
possibly support showing ongoing matches too in the future. It's only
supported for ranked play right now, though there is no reason we
couldn't track quick play rooms too (it's just... I'm not sure how to
design the panels for quick play).
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
In human words: I read [this forum
thread](https://osu.ppy.sh/community/forums/topics/2195138?n=1) today
and was horrified to see the user there opening the F3 options menu in
song select *when the F1 mod overlay was pulled up*, which is (a) not
intended UX, (b) looks terrible, and (c) just wrecks the game
behaviourally wholesale from start to end.
So with this change you don't get to open options via F3 while inside
mod overlay at all.
The `Action` shadowing is pretty ugly but I don't have better ideas.
Initially I tried to mess with `Enabled` (as I did once previously, see
https://github.com/ppy/osu/commit/36628e24f92c286b87c118c7c1bb9bc582895571),
but it's much more complicated in this case because the enabled state
needs to be restored when the buttons reappear, or it could change
independently while the buttons are temporarily hidden, etc. So I'd
rather just not deal with all that and invent a parallel scheme.
This was a private request for roundtable event usage. It’s also a
common feature request, so I decided to spend a bit of time getting this
working well-enough.
https://github.com/user-attachments/assets/acceb57f-2979-43d0-9fc2-33e977bd2dd5
---
### Delay loading spinner / loading layer initial load briefly to avoid
flickering
There's cases in this overlay where loading takes a few milliseconds.
The loading spinner gets annoying. This also happens elsewhere, so this
could be considered a global fix. Separate PR? probably...
### Ingest loading state of dashboard child content to show more correct
loading layer
Each display had their own loading layer implementation, but this is
already too deep (inside the scroll content) and doesn't display great
when for instance, results don't take up the full screen height.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
This was used in one place, but I foresee this being a more common
scenario. This also fixes an edge case where the dismiss process would
fail if completion happened on an async thread before the
`NotificationOverlay`'s scheduler could handle the initial ingress.
`RankedPlaySubScreen.CenterColumn` had a padding which moved the card
hand up slightly, causing it to not fully dissapear when contracting.
The padding doesn't serve any purpose anymore (remnant of the very early
versions of the screens), so I just removed it.
I checked against `DiscardScreen`, `PickScreen`, `OpponentPickScreen` &
`EndedScreen` to make sure this doesn't cause any layout breakage.
Also removed the `ButtonsContainer` since it isn't being used anywhere
anymore.
https://github.com/user-attachments/assets/2fd32407-fbf7-45a3-b92a-0730a0f8a3fd
## [Rewrite `BackgroundMusicManager` to not run into framework
breakage](https://github.com/ppy/osu/commit/622216d8911832c39fa4e126b2810e4e0f46cbf7)
The attempted proper fix to this was
https://github.com/ppy/osu-framework/pull/6727. Unfortunately when
presented with [the framework
bump](https://github.com/ppy/osu/pull/37217) with that change, CI says
"you're stupid" and fails on some disposal idiocy that of course is
undebuggable and irreproducible:
The active test run was aborted. Reason: Test host process crashed :
Unhandled exception. System.AggregateException: One or more errors
occurred. (Object reference not set to an instance of an object.)
---> System.NullReferenceException: Object reference not set to an
instance of an object.
at osu.Framework.Audio.Sample.SampleChannelBass.Dispose(Boolean
disposing)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
at System.Threading.Tasks.Task.ExecuteWithThreadLocal(Task&
currentTaskSlot, Thread threadPoolThread)
--- End of inner exception stack trace ---
at osu.Framework.Audio.AudioCollectionManager`1.UpdateChildren()
at osu.Framework.Audio.AudioCollectionManager`1.UpdateChildren()
at osu.Framework.Audio.AudioCollectionManager`1.UpdateChildren()
at osu.Framework.Audio.AudioCollectionManager`1.UpdateChildren()
at osu.Framework.Threading.AudioThread.OnExit()
at osu.Framework.Threading.GameThread.setExitState(GameThreadState
exitState)
at osu.Framework.Threading.GameThread.RunSingleFrame()
at osu.Framework.Threading.GameThread.<createThread>g__runWork|70_0()
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
--- End of stack trace from previous location ---
at System.Threading.ExecutionContext.RunInternal(ExecutionContext
executionContext, ContextCallback callback, Object state)
(https://github.com/ppy/osu/actions/runs/24019928154/job/70046733058?pr=37217#step:5:119)
I no longer have the energy for any of this shit.
@nekodex would appreciate if you could check that I actually haven't
broken anything with the bgm here. Seems okay to me in test scenes at
least.
## [Apply lowest-effort maybe-fixing changes to a bunch of flaking
tests](https://github.com/ppy/osu/commit/7bd3ca4adfcce5b90add11565a13f3fe9177ad5e)
None of the failures are reproducible locally, of course. I'm tired of
this. If anyone else wants to subject themselves to actually
investigating any of these, by all means, godspeed and good luck.
Displays the stage name and details like currently picking player
and damage multiplayer where applicable.
Currently only shown on the discard and pick stages.
# Move user retrieval to `RankedPlayScreen`
This is done because I need the relevant `APIUser` instances in order to
pass them to the overlay component. `RankedPlayScreen` seems like the
appropriate place to manage the overlays since it manages the stage
subscreens, hence the need to access the `APIUser`s in here.
# Add current stage overlay to ranked play
The actual change of this PR. Very much a dev design.
https://github.com/user-attachments/assets/2388e934-2fc7-4e15-9947-9f98412765d2
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
This has gone through a few iterations, and eventually ended up as a
simple text percentage display next to the username. I feel that adding
another progress bar right next to the big healthbar would make things
too cluttered, and trying to move the beatmap state elsewhere would make
it too disconnected from the players that are potentially downloading a
beatmap.
I considered making the local user fetch download progress data using
`BeatmapDownloadTracker` instead of relying on `BeatmapAvailability` in
order to get more frequent updates, but that would add a lot of extra
complexity for little gain IMO.
[Screencast_20260403_095644.webm](https://github.com/user-attachments/assets/85fbd4b8-6b5c-41d2-b29b-c93885f73bb3)
- Last part of / closes
https://github.com/ppy/osu-server-spectator/issues/406.
- Remaining work on slots will be tracked in
https://github.com/ppy/osu-server-spectator/issues/405.
This PR is a corollary of
https://github.com/ppy/osu-server-spectator/pull/453 and all of the
dispensations referee users in a multiplayer have received therein. The
goal here is to allow access to all relevant room management functions
even if the referee in question isn't host, as well as to disallow
access to all non-relevant functions to do with the actual match
gameplay.
I'm not going to lie, this logic *is* ugly. I would argue that it
already *was* ugly on `master` and my goal was to operate with as light
a touch as possible myself. But you could see this as copping out and
that I should try to refactor some of this. I will try - but only after
someone else's seen the initial approach and deemed it unsuitable.
The logic in `MatchStartControl` is awful - there are so many moving
pieces of state that dictate what can happen when with all the buttons,
and yes, I am making it worse here.
This time there is some test coverage. Not everything is covered, but
the coverage should be on par in all components and pieces of relevant
logic I touched that already had tests covering them. On that note,
please forgive the diffstat size, but the tests *are* most of that size.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Intended to add toggle but forgot.
This also fixes https://github.com/ppy/osu/issues/37012 via a convoluted
refactor of a lot of stuff. The basic overview is:
- Moved all replay overlay concerns out of `HUDOverlay`. We can display
this above everything confidently (i think).
- Split out `ReplayOverlay` and `ReplaySettingsOverlay` so the base
class can handle the visibility, hotkeys and everything that should be
shared with *all* replay overlay components going forward. `Ctrl+H` is
supposed to hide any of these kinds of details, and I'm sure we'll add
more in the future.
- Reorganised some things in `Player` so the new structure would work.
Mainly the overlays which add a black layer during fade out.
Adds the ability to drag and reorder cards. Card order is preserved
between rounds and is synchronized between both players (each player can
see the other player drag around and reorder their cards).
To make this possible I had to rewrite the card layout algorithm to be
stateless (e0a46fefaf), there wasn't
really a way around that since I needed a way to calculate a layout
position based on a card's index. Should hopefully be a lot easier to
read now though.
Some noteworthy stuff:
- I didn't really know what the best place to store the card order is,
so I put it on `RankedPlayCardWithPlaylistItem` since that one will stay
the same instance per round.
- To prevent the opponent's cards from dragged into the middle of the
playfield, only the x axis of the drag gets synchronized for the
`OpponentHandOfCards` with a fixed y value.
- I adjusted the replay recorder/player parameters a little. With the
drag events happening every frame the replay recorder record new frames
every 25ms and end up dropping half the replay frames per flush
interval, so I increased the sample interval to 50ms so the buffer size
matches the sample rate exactly (50ms -> 20 samples per flush every
1000ms).
I also increased the buffer size in the replay player a bit so slight
fluctuations in latency won't make it start to drop frames.
https://github.com/user-attachments/assets/b810cb85-db02-4edf-a63e-bfc96cf59665https://github.com/user-attachments/assets/4d2f884d-fcce-4948-9659-fbb314634cb8
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
The fix is just disabling the animation. It works I guess.
---
- Closes https://github.com/ppy/osu/issues/37042
Currently in Mania, you can change the scroll speed for a brief period
during the beginning of a song. However this scroll speed change occurs
over a short period of time, which causes a bunch of extra hit object
updates, causing major fps and latency drops.
This fix simply replaces the dampening with an immediate scroll speed
update. Since the scroll speed can only be updated for a short time at
the beginning of the song, providing immediate visual feedback on the
scroll speed makes sense to me. However another potential solution would
be to filter the TimeRange Value updates to keep the gradual scroll
speed visual change, while greatly reducing the number of updates to the
hit objects currently on screen.
If there is any feedback I would greatly appreciate it as this is my
first issue here. I had ran both inspectCode.ps1 and the code formatter
before creating the merge request. Thank you.
Before fix:
https://github.com/user-attachments/assets/55e30894-7341-414a-af2e-2ec051c3a252
After fix:
https://github.com/user-attachments/assets/c085d33f-c0ae-45dd-8131-e79a5682b9ca
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
In discussions, we've come to the conclusion to attempt to use a
chat-bubble system to minimise the effective area of the chat. In
particular, the results screen doesn't give us enough space to display
the full chat box without overlapping the main screen content.
This PR both adds the chat to the results screen, and reworks it to use
such a bubble system (not sure what to call it, IM style?).
https://github.com/user-attachments/assets/a8a88c51-8a9d-4a03-92b6-621112a15a41
- New messages are previewed for 3 seconds.
- When focusing and unfocusing the textbox, the history moves into
expanded state (show the most recent 10 messages) or collapsed state
(fade messages out ASAP).
This is a bit of an initial implementation to get a feel of how it
behaves, and there's more that can be done such as adding colours,
improving the transforms, perhaps adding it to the intro screen
(post-animation) but the structure's a bit weird atm.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
This was mentioned in vivi's feedback. Basically now the card backside
is always loaded and there, rather than faffing with switching the
content around.
> Cards have a stale grey background when they are spawning in before
they get changed into the cards they’re supposed to be. This can be
changed to the backside of the card or maybe a bright white card. Makes
it look less placeholdery.
Keeping it simple for now. Can probably hide it when not in use in the
future.
Specifically the one used on the daily challenge screen. Was bugging me
that playlists/multi have that old yellow header design used, so I've
changed it. Will probably come in handy once the footer/leaderboard
changes are in.
Also localized the headers while at it.
Multiplayer:

Playlists:

The only thing I'm wondering about is whether the detail thing
(participant count/playlist length) should be using the highlight colour
here. The old design had them be yellow, but I feel like the pink on
this one stands out too much.
---------
Co-authored-by: Dean Herbert <pe@ppy.sh>
Right click is a very obfuscated UX which most users won't find. This
makes more sense to the average user (probably).
Caveat: clicking away actually sends clicks to underlying UI. This is
not the case in macOS or windows (locally, same app; globally it still
sends clicks to other windows).
Coming from https://github.com/ppy/osu/discussions/36926.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
https://github.com/user-attachments/assets/042311f1-b8c3-479b-a173-13b93fe2c5cc
- `/roll` command is now supported in multiplayer chat for all players
(don't need to be a referee).
- Referees are shown in the room with a special status.
- Tournament mode rooms can be locked, which prevents users from
changing team (and slot, whenever those get brought back). When the room
is locked, the user team indicator shows a padlock icon on top to
indicate the lock state.
---
- Related: https://github.com/ppy/osu-server-spectator/issues/406
Grab-bag because I really don't think splitting this into 3 PRs is very
helpful.
I was going to add an animation for rolling but I had a go on Friday and
my attempt got more or less the same reception as a wet fart so I'm not
trying again. Someone else can if so inclined. I have completely lost
trust in my design senses.
Contrary to stable the roll results are completely ephemeral and go away
when the room is re-entered. This could be both considered good and bad.
Not for me to say.
Arguably we should just nuke the now playing overlay playlist window
completely as the functionality was gimped when search was removed. Now
it doesn't seek to the currently playing track, nor play sequentially,
nor do anything useful.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
It's been a while.
Notes:
- `SharpCompress` usages changed a bit. Manually adjusted these, mostly
just renames or adjusted parameters.
- nUnit 3 -> 4 migrated using
https://gist.github.com/peppy/07994386d793a117350cb5f24b156585. there's
a mode in this script to update to the newer `Assert.That` syntax but it
requires fixes and couldn't really be bothered.
- DeepEqual nuked as the only usage was on a disabled test. The reason
it's disabled has been merged upstream, but it's failing for other
(realm) reasons which I don't think is worthwhile to investigate for
now.
- This bumps Moq. I think the author is back in a sensible headspace and
the new version has the stupid shit removed, so probably okay? Nice to
be on a level playing field with packages for once in a long time.
- Automapper is silly, but we've discussed this elsewhere.
- `TestRealmKeyBindingStore` failures are a wildcard, but fixed by using
a more standardised testing method. Dunno why, don't care.
---------
Co-authored-by: Bartłomiej Dach <dach.bartlomiej@gmail.com>
As mentioned in https://github.com/ppy/osu/discussions/36883.
This has caught me off-guard a few times.
Was a quick one to make this work like it does on stable. It doesn't fit
as well as stable because we have a lot of elements at the top of the
screen, but I think it's better than nothing, as it lets you know you're
in a replay quick obviously.
I don't think we can easily localise strings with formatting in them
yet. Maybe using a `MarkdownContainer` or something?