Consider "combo offsets" as legacy logic and separate from combo information

This commit is contained in:
Salman Ahmed 2021-05-05 07:27:57 +03:00
parent eeeb001d62
commit cd6d070b4a
17 changed files with 56 additions and 54 deletions

View File

@ -38,7 +38,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
RepeatCount = curveData.RepeatCount, RepeatCount = curveData.RepeatCount,
X = positionData?.X ?? 0, X = positionData?.X ?? 0,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0 LegacyLastTickOffset = (obj as IHasLegacyLastTickOffset)?.LegacyLastTickOffset ?? 0
}.Yield(); }.Yield();
@ -49,7 +48,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
Samples = obj.Samples, Samples = obj.Samples,
Duration = endTime.Duration, Duration = endTime.Duration,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
}.Yield(); }.Yield();
default: default:
@ -58,7 +56,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
StartTime = obj.StartTime, StartTime = obj.StartTime,
Samples = obj.Samples, Samples = obj.Samples,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0,
X = positionData?.X ?? 0 X = positionData?.X ?? 0
}.Yield(); }.Yield();
} }

View File

@ -67,8 +67,6 @@ namespace osu.Game.Rulesets.Catch.Objects
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }
public int ComboOffset { get; set; }
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>(); public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
public int IndexInCurrentCombo public int IndexInCurrentCombo

View File

@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
RepeatCount = curveData.RepeatCount, RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0, LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0,
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset, LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
// this results in more (or less) ticks being generated in <v8 maps for the same time duration. // this results in more (or less) ticks being generated in <v8 maps for the same time duration.
@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
EndTime = endTimeData.EndTime, EndTime = endTimeData.EndTime,
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2, Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0, LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0,
}.Yield(); }.Yield();
default: default:
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
Samples = original.Samples, Samples = original.Samples,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,
ComboOffset = comboData?.ComboOffset ?? 0, LegacyBeatmapComboOffset = (original as IHasLegacyBeatmapComboOffset)?.LegacyBeatmapComboOffset ?? 0,
}.Yield(); }.Yield();
} }
} }

View File

@ -14,7 +14,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
{ {
public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition public abstract class OsuHitObject : HitObject, IHasComboInformation, IHasPosition, IHasLegacyBeatmapComboOffset
{ {
/// <summary> /// <summary>
/// The radius of hit objects (ie. the radius of a <see cref="HitCircle"/>). /// The radius of hit objects (ie. the radius of a <see cref="HitCircle"/>).
@ -73,14 +73,6 @@ namespace osu.Game.Rulesets.Osu.Objects
public virtual bool NewCombo { get; set; } public virtual bool NewCombo { get; set; }
public readonly Bindable<int> ComboOffsetBindable = new Bindable<int>();
public int ComboOffset
{
get => ComboOffsetBindable.Value;
set => ComboOffsetBindable.Value = value;
}
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>(); public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();
public virtual int IndexInCurrentCombo public virtual int IndexInCurrentCombo
@ -105,6 +97,10 @@ namespace osu.Game.Rulesets.Osu.Objects
set => LastInComboBindable.Value = value; set => LastInComboBindable.Value = value;
} }
public int LegacyBeatmapComboOffset { get; set; }
public int LegacyBeatmapComboIndex { get; set; }
protected OsuHitObject() protected OsuHitObject()
{ {
StackHeightBindable.BindValueChanged(height => StackHeightBindable.BindValueChanged(height =>

View File

@ -82,7 +82,6 @@ namespace osu.Game.Tests.Gameplay
private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation
{ {
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset => 0;
public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>(); public Bindable<int> IndexInCurrentComboBindable { get; } = new Bindable<int>();

View File

@ -37,7 +37,7 @@ namespace osu.Game.Beatmaps
if (obj.NewCombo) if (obj.NewCombo)
{ {
obj.IndexInCurrentCombo = 0; obj.IndexInCurrentCombo = 0;
obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + obj.ComboOffset + 1; obj.ComboIndex = (lastObj?.ComboIndex ?? 0) + 1;
if (lastObj != null) if (lastObj != null)
lastObj.LastInCombo = true; lastObj.LastInCombo = true;
@ -48,6 +48,16 @@ namespace osu.Game.Beatmaps
obj.ComboIndex = lastObj.ComboIndex; obj.ComboIndex = lastObj.ComboIndex;
} }
if (obj is IHasLegacyBeatmapComboOffset legacyObj)
{
var lastLegacyObj = (IHasLegacyBeatmapComboOffset)lastObj;
if (obj.NewCombo)
legacyObj.LegacyBeatmapComboIndex = (lastLegacyObj?.LegacyBeatmapComboIndex ?? 0) + legacyObj.LegacyBeatmapComboOffset + 1;
else if (lastLegacyObj != null)
legacyObj.LegacyBeatmapComboIndex = lastLegacyObj.LegacyBeatmapComboIndex;
}
lastObj = obj; lastObj = obj;
} }
} }

View File

@ -292,10 +292,11 @@ namespace osu.Game.Beatmaps.Formats
if (hitObject is IHasCombo combo) if (hitObject is IHasCombo combo)
{ {
type = (LegacyHitObjectType)(combo.ComboOffset << 4);
if (combo.NewCombo) if (combo.NewCombo)
type |= LegacyHitObjectType.NewCombo; type |= LegacyHitObjectType.NewCombo;
if (hitObject is IHasLegacyBeatmapComboOffset comboOffset)
type |= (LegacyHitObjectType)(comboOffset.LegacyBeatmapComboOffset << 4);
} }
switch (hitObject) switch (hitObject)

View File

@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X { get; set; } public float X { get; set; }
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset { get; set; }
} }
} }

View File

@ -18,21 +18,16 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
} }
private bool forceNewCombo; private bool forceNewCombo;
private int extraComboOffset;
protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset)
{ {
newCombo |= forceNewCombo; newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
forceNewCombo = false; forceNewCombo = false;
extraComboOffset = 0;
return new ConvertHit return new ConvertHit
{ {
X = position.X, X = position.X,
NewCombo = newCombo, NewCombo = newCombo,
ComboOffset = comboOffset
}; };
} }
@ -40,16 +35,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
List<IList<HitSampleInfo>> nodeSamples) List<IList<HitSampleInfo>> nodeSamples)
{ {
newCombo |= forceNewCombo; newCombo |= forceNewCombo;
comboOffset += extraComboOffset;
forceNewCombo = false; forceNewCombo = false;
extraComboOffset = 0;
return new ConvertSlider return new ConvertSlider
{ {
X = position.X, X = position.X,
NewCombo = FirstObject || newCombo, NewCombo = FirstObject || newCombo,
ComboOffset = comboOffset,
Path = new SliderPath(controlPoints, length), Path = new SliderPath(controlPoints, length),
NodeSamples = nodeSamples, NodeSamples = nodeSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
@ -58,11 +49,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration)
{ {
// Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo
// Their combo offset is still added to that next hitobject's combo index
forceNewCombo |= FormatVersion <= 8 || newCombo;
extraComboOffset += comboOffset;
return new ConvertSpinner return new ConvertSpinner
{ {
Duration = duration Duration = duration

View File

@ -13,7 +13,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X { get; set; } public float X { get; set; }
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset { get; set; }
} }
} }

View File

@ -17,7 +17,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
public float X => 256; // Required for CatchBeatmapConverter public float X => 256; // Required for CatchBeatmapConverter
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset { get; set; }
} }
} }

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary> /// <summary>
/// Legacy osu! Hit-type, used for parsing Beatmaps. /// Legacy osu! Hit-type, used for parsing Beatmaps.
/// </summary> /// </summary>
internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset
{ {
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset { get; set; } public int LegacyBeatmapComboOffset { get; set; }
int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; }
} }
} }

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
{ {
Position = position, Position = position,
NewCombo = FirstObject || newCombo, NewCombo = FirstObject || newCombo,
ComboOffset = comboOffset LegacyBeatmapComboOffset = comboOffset
}; };
} }
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
{ {
Position = position, Position = position,
NewCombo = FirstObject || newCombo, NewCombo = FirstObject || newCombo,
ComboOffset = comboOffset, LegacyBeatmapComboOffset = comboOffset,
Path = new SliderPath(controlPoints, length), Path = new SliderPath(controlPoints, length),
NodeSamples = nodeSamples, NodeSamples = nodeSamples,
RepeatCount = repeatCount RepeatCount = repeatCount

View File

@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
/// <summary> /// <summary>
/// Legacy osu! Slider-type, used for parsing Beatmaps. /// Legacy osu! Slider-type, used for parsing Beatmaps.
/// </summary> /// </summary>
internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo, IHasLegacyBeatmapComboOffset
{ {
public Vector2 Position { get; set; } public Vector2 Position { get; set; }
@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset { get; set; } public int LegacyBeatmapComboOffset { get; set; }
int IHasLegacyBeatmapComboOffset.LegacyBeatmapComboIndex { get; set; }
} }
} }

View File

@ -22,7 +22,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
public float Y => Position.Y; public float Y => Position.Y;
public bool NewCombo { get; set; } public bool NewCombo { get; set; }
public int ComboOffset { get; set; }
} }
} }

View File

@ -12,10 +12,5 @@ namespace osu.Game.Rulesets.Objects.Types
/// Whether the HitObject starts a new combo. /// Whether the HitObject starts a new combo.
/// </summary> /// </summary>
bool NewCombo { get; } bool NewCombo { get; }
/// <summary>
/// When starting a new combo, the offset of the new combo relative to the current one.
/// </summary>
int ComboOffset { get; }
} }
} }

View File

@ -0,0 +1,24 @@
// 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.
namespace osu.Game.Rulesets.Objects.Types
{
/// <summary>
/// A type of <see cref="HitObject"/> that has a combo index with arbitrary offsets applied to use when retrieving legacy beatmap combo colours.
/// This is done in stable for hitobjects to skip combo colours from the beatmap skin (known as "colour hax").
/// See https://osu.ppy.sh/wiki/en/osu%21_File_Formats/Osu_%28file_format%29#type for more information.
/// </summary>
public interface IHasLegacyBeatmapComboOffset
{
/// <summary>
/// The legacy offset of the new combo relative to the current one, when starting a new combo.
/// </summary>
int LegacyBeatmapComboOffset { get; }
/// <summary>
/// The combo index with the <see cref="LegacyBeatmapComboOffset"/> applied,
/// to use for legacy beatmap skins to decide on the combo colour.
/// </summary>
int LegacyBeatmapComboIndex { get; set; }
}
}