This change pulls back a significant degree of overspecialisation and
rigidity in the class structure of `HitWindows` to make subsequent
changes to hit windows, whose purpose is to improve replay playback
accuracy, possible to do cleanly.
Notably:
- `HitWindows` is full abstract now. In a few use cases, and as a
reference for ruleset implementors, `DefaultHitWindows` is provided as
a separate class instead.
This fixes the weirdness wherein `HitWindows` always declared 6 fields
for result types but some of them would never be set to a non-zero
value or read.
- `HitWindow.GetRanges()` is deleted because it is overspecialised and
prevents being able to adjust hitwindows by ±0.5ms cleanly which will
be required later.
The fallout of this is that the assertion that used `GetRanges()` in
the `HitWindows` ctor must use something else now, and the closest
thing to it was `GetAllAvailableWindows()`, which didn't return
the miss window - so I made it return the miss window and fixed the
one consumer that didn't want it (bar hit error meter) to skip it.
- Diff also contains some clean-up around `DifficultyRange` to unify
handling of it.
Previously, random selection would always be done at a *set* level. The
final operation of a random action would be "select the user's
recommended difficulty from this randomly selected set".
This makes no sense when sets are not grouped together at song select.
In fact, it is completely broken with the previous commit which adds
group-isolated random support – if we're grouping by difficulty and the
user's recommendation is not in the current group it would throw the
user into another group unexpectedly.
This fixes the issue by splitting out the random implementation into two
separate pathways depending on the carousel display mode.