In particular, mania-specific beatmaps that normally go via the
"passthrough" generator should not adjust the stored pattern value.
The "spinner" generator, which was previously intended to be used for
non-mania-specific beatmaps, is now valid even for mania-specific
beatmaps, and uses this value.
In other words, another way of writing this would be:
```csharp
if (conversion is SpinnerPatternGenerator || conversion is
PassThroughPatternGenerator) ? lastPattern : newPattern;
```
Better symbolises the intent of this generator which is to convert
hitobjects in their most simple forms - anything with an end time
converts to a hold or otherwise converts to a normal note.
A big part of these changes is refactoring, which is somewhat necessary
because it was previously implemented as two separate pathways which
in-fact need to be joined at the hip when handling spinners.
I've chosen to use `IHasLegacyHitObjectType` here because there's no
other flag that allows us to tell `ConvertHold` apart from
`ConvertSpinner`.
In order for the new star difficulty to be shown to users on the next
release.
catch's difficulty calculator version is not bumped because the only
catch change pending deploy is https://github.com/ppy/osu/pull/28353 and
that affects performance points only.
Closes https://github.com/ppy/osu/issues/29793.
I believe that the sequence of events that makes this happens is as
follows:
- User selects a range of objects. Some of those objects are off-screen,
and thus would be presumed to be not alive - except the blueprint
container forces them to remain alive, because they're part of the
selection.
- User moves the selection to another column, which is implemented by
temporarily removing the objects from the playfield, changing their
column, and re-adding them.
This sort of pattern is supposed to kick off the
`HitObjectUsageTransferred` flow in `HitObjectUsageEventBuffer` - and
it does... for objects that are *currently visible on screen* and thus
would be alive regardless of `SetKeepAlive()`. However, this does not
hold for objects that are off-screen - nothing ensures they are kept
alive again after re-adding, and thus they inadvertently become dead.
- Thus, this doesn't kick off the `BlueprintContainer` flows associated
with transferring objects to another column, and instead fires the
removal flows, which ensure that the off-screen objects that were
being moved are instead deselected.
I tried a few other options but found no better resolution than this -
calling `SetKeepAlive()` directly would require making it public, which
seems like a bad idea. There's really no good way to generically handle
this either, because it is the ruleset that decides that its way of
implementing this operation will be a removal and re-add of objects,
so...