The gist of the issue is that `fadeOut` was being called *twice* in the
quick exit/retry scenarios, causing weirdness with transforms.
I've restructured things to ensure it's only called once.
I don't know why this was ever a good idea, and would say that we want
this to fail *hard* not soft. If things ever get in this state, things
have gone *seriously* wrong elsewhere, and need to be fixed there.
I've been meaning to make the progress bar synchronise with the beat
rather than a continuous countdown, just to give the overlay a bit more
of a rhythmic feel.
Not completely happy with how this feels but I think it's a start?
I had to refactor how the break overlay works in the process. It no
longer creates transforms for all breaks ahead-of-time, which could be
argued as a better way of doing things. It's more dynamically able to
handle breaks now (maybe useful for the future, who knows).
I didn't really want to move the cursor in front of the HUD, but we face
a bit of an impossible scenario otherwise (it should definitely be in
front of the break overlay for visibility).
So I'll deal with it for now.
Closes https://github.com/ppy/osu/issues/6842.
This is a rather barebones implementation, just to get this in place
somehow at least. The logic is simple - 50% health or above shows pass
layer, anything below shows fail layer.
This does not match stable logic all across the board because I have
no idea how to package that. Stable defines "passing" in like fifty
ways:
- in mania it's >80% HP
(bb57924c15/osu!/GameModes/Play/Rulesets/Mania/RulesetMania.cs#L333-L336)
- in taiko it's >80% *accuracy*
(bb57924c15/osu!/GameModes/Play/Rulesets/Taiko/RulesetTaiko.cs#L486-L492)
- there's also the part where "geki additions" will unconditionally set
passing state
(bb57924c15/osu!/GameModes/Play/Player.cs#L3561-L3564)
- and also the part where at the end of the map, the final passing state
is determined by checking whether the user passed more sections than
failed
(bb57924c15/osu!/GameModes/Play/Player.cs#L3320)
The biggest issues of these are probably the first two, and they can
*probably* be fixed, but would require a new member on `Ruleset` and I'm
not sure how to make one look, so I'm not doing that at this time
pending collection of ideas on how to do that.
Originally when popping in, the ReplayPlayer was loaded first (if previous screen was MainMenu), and afterwards the SkinEditor component was loaded asynchronously. However, if the ReplayPlayer screen exits quickly (like in the event the beatmap has no objects), the skin editor component has not finished initializing (this is before it was even added to the component tree, so it's still not marked `Visible`), then the screen exiting will cause `OsuGame` to call SetTarget(newScreen) -> setTarget(...) which sees that the cached `skinEditor` is not visible yet, and hides/nulls the field. This is the point where LoadComponentAsync(editor, ...) finishes, and the callback sees that the cached skinEditor field is now different (null) than the one that was loaded, and never adds it to the component tree. This occurrence is unhandled and as such the SkinEditorOverlay never hides itself, consuming all input infinitely.
This PR changes the loading to start loading the ReplayPlayer *after* the SkinEditor has been loaded and added to the component tree.
Additionally, this lowers the exit delay for ReplayPlayer and changes the "no hit objects" notification to not be an error since it's a controlled exit.
The import preparation task can actually be non-null when exiting even
if the player hasn't passed:
- fail beatmap
- click import button to import the failed replay