Fix slider nodes using the wrong samples

This commit is contained in:
smoogipoo 2018-10-16 17:10:24 +09:00
parent 33d4ec876a
commit 47be95ce0b
17 changed files with 109 additions and 30 deletions

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
ControlPoints = curveData.ControlPoints, ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType, CurveType = curveData.CurveType,
Distance = curveData.Distance, Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples, NodeSamples = curveData.NodeSamples,
RepeatCount = curveData.RepeatCount, RepeatCount = curveData.RepeatCount,
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH, X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,

View File

@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Catch.Objects
set { Curve.ControlPoints = value; } set { Curve.ControlPoints = value; }
} }
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>(); public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public CurveType CurveType public CurveType CurveType
{ {

View File

@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount(); double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index]; return curveData.NodeSamples[index];
} }
} }
} }

View File

@ -470,7 +470,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
double segmentTime = (EndTime - HitObject.StartTime) / spanCount; double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
return curveData.RepeatSamples[index]; return curveData.NodeSamples[index];
} }
/// <summary> /// <summary>

View File

@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = 700, Distance = 700,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats), NodeSamples = createEmptySamples(repeats),
StackHeight = 10 StackHeight = 10
}; };
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = distance, Distance = distance,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats), NodeSamples = createEmptySamples(repeats),
StackHeight = stackHeight StackHeight = stackHeight
}; };
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = 600, Distance = 600,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats) NodeSamples = createEmptySamples(repeats)
}; };
addSlider(slider, 2, 3); addSlider(slider, 2, 3);
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = 793.4417, Distance = 793.4417,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats) NodeSamples = createEmptySamples(repeats)
}; };
addSlider(slider, 2, 3); addSlider(slider, 2, 3);
@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = 480, Distance = 480,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats) NodeSamples = createEmptySamples(repeats)
}; };
addSlider(slider, 2, 3); addSlider(slider, 2, 3);
@ -246,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = 1000, Distance = 1000,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = createEmptySamples(repeats) NodeSamples = createEmptySamples(repeats)
}; };
addSlider(slider, 2, 3); addSlider(slider, 2, 3);
@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
Distance = 300, Distance = 300,
RepeatCount = repeats, RepeatCount = repeats,
RepeatSamples = repeatSamples NodeSamples = repeatSamples
}; };
addSlider(slider, 3, 1); addSlider(slider, 3, 1);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
ControlPoints = curveData.ControlPoints, ControlPoints = curveData.ControlPoints,
CurveType = curveData.CurveType, CurveType = curveData.CurveType,
Distance = curveData.Distance, Distance = curveData.Distance,
RepeatSamples = curveData.RepeatSamples, NodeSamples = curveData.NodeSamples,
RepeatCount = curveData.RepeatCount, RepeatCount = curveData.RepeatCount,
Position = positionData?.Position ?? Vector2.Zero, Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false, NewCombo = comboData?.NewCombo ?? false,

View File

@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Osu.Objects
/// </summary> /// </summary>
internal float LazyTravelDistance; internal float LazyTravelDistance;
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>(); public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public int RepeatCount { get; set; } public int RepeatCount { get; set; }
/// <summary> /// <summary>
@ -129,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
StartTime = StartTime, StartTime = StartTime,
Position = Position, Position = Position,
Samples = Samples, Samples = NodeSamples[0],
SampleControlPoint = SampleControlPoint, SampleControlPoint = SampleControlPoint,
IndexInCurrentCombo = IndexInCurrentCombo, IndexInCurrentCombo = IndexInCurrentCombo,
ComboIndex = ComboIndex, ComboIndex = ComboIndex,
@ -209,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Objects
Position = Position + Curve.PositionAt(repeat % 2), Position = Position + Curve.PositionAt(repeat % 2),
StackHeight = StackHeight, StackHeight = StackHeight,
Scale = Scale, Scale = Scale,
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex]) Samples = new List<SampleInfo>(NodeSamples[1 + repeatIndex])
}); });
} }
} }

View File

@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{ {
List<List<SampleInfo>> allSamples = curveData != null ? curveData.RepeatSamples : new List<List<SampleInfo>>(new[] { samples }); List<List<SampleInfo>> allSamples = curveData != null ? curveData.NodeSamples : new List<List<SampleInfo>>(new[] { samples });
int i = 0; int i = 0;
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing) for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)

View File

@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Legacy;
using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -312,5 +313,48 @@ namespace osu.Game.Tests.Beatmaps.Formats
SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]); SampleInfo getTestableSampleInfo(HitObject hitObject) => hitObject.SampleControlPoint.ApplyTo(hitObject.Samples[0]);
} }
[Test]
public void TestDecodeSliderSamples()
{
var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false };
using (var resStream = Resource.OpenResource("slider-samples.osu"))
using (var stream = new StreamReader(resStream))
{
var hitObjects = decoder.Decode(stream).HitObjects;
var slider1 = (ConvertSlider)hitObjects[0];
Assert.AreEqual(1, slider1.NodeSamples[0].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[0][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[1].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[1][0].Name);
Assert.AreEqual(1, slider1.NodeSamples[2].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider1.NodeSamples[2][0].Name);
var slider2 = (ConvertSlider)hitObjects[1];
Assert.AreEqual(2, slider2.NodeSamples[0].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[0][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[0][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[1].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[1][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[1][1].Name);
Assert.AreEqual(2, slider2.NodeSamples[2].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider2.NodeSamples[2][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider2.NodeSamples[2][1].Name);
var slider3 = (ConvertSlider)hitObjects[2];
Assert.AreEqual(2, slider3.NodeSamples[0].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[0][0].Name);
Assert.AreEqual(SampleInfo.HIT_WHISTLE, slider3.NodeSamples[0][1].Name);
Assert.AreEqual(1, slider3.NodeSamples[1].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[1][0].Name);
Assert.AreEqual(2, slider3.NodeSamples[2].Count);
Assert.AreEqual(SampleInfo.HIT_NORMAL, slider3.NodeSamples[2][0].Name);
Assert.AreEqual(SampleInfo.HIT_CLAP, slider3.NodeSamples[2][1].Name);
}
}
} }
} }

View File

@ -0,0 +1,23 @@
osu file format v14
[General]
SampleSet: Normal
[Difficulty]
SliderMultiplier:1.6
SliderTickRate:2
[TimingPoints]
24735,389.61038961039,4,1,1,25,1,0
[HitObjects]
// Unified: Normal, Normal, Normal
168,256,30579,6,0,B|248:320|320:248,2,160
// Unified: Normal+Clap, Normal+Clap, Normal+Clap
168,256,32137,6,8,B|248:320|320:248,2,160
// Nodal: Normal+Whistle, Normal, Normal+Clap
// Nodal sounds should override the unified clap sound
168,256,33696,6,8,B|248:320|320:248,2,160,2|0|8

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
}; };
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{ {
newCombo |= forceNewCombo; newCombo |= forceNewCombo;
comboOffset += extraComboOffset; comboOffset += extraComboOffset;
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = length, Distance = length,
CurveType = curveType, CurveType = curveType,
RepeatSamples = repeatSamples, NodeSamples = nodeSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };
} }

View File

@ -169,6 +169,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
result = CreateSlider(pos, combo, comboOffset, points, length, curveType, repeatCount, nodeSamples); result = CreateSlider(pos, combo, comboOffset, points, length, curveType, repeatCount, nodeSamples);
// The samples are played when the slider ends, which is the last node
result.Samples = nodeSamples[nodeSamples.Count - 1];
} }
else if (type.HasFlag(ConvertHitObjectType.Spinner)) else if (type.HasFlag(ConvertHitObjectType.Spinner))
{ {
@ -200,7 +203,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
} }
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset; result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + Offset;
result.Samples = convertSoundType(soundType, bankInfo);
if (result.Samples.Count == 0)
result.Samples = convertSoundType(soundType, bankInfo);
FirstObject = false; FirstObject = false;
@ -260,9 +265,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
/// <param name="length">The slider length.</param> /// <param name="length">The slider length.</param>
/// <param name="curveType">The slider curve type.</param> /// <param name="curveType">The slider curve type.</param>
/// <param name="repeatCount">The slider repeat count.</param> /// <param name="repeatCount">The slider repeat count.</param>
/// <param name="repeatSamples">The samples to be played when the repeat nodes are hit. This includes the head and tail of the slider.</param> /// <param name="nodeSamples">The samples to be played when the slider nodes are hit. This includes the head and tail of the slider.</param>
/// <returns>The hit object.</returns> /// <returns>The hit object.</returns>
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples); protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples);
/// <summary> /// <summary>
/// Creates a legacy Spinner-type hit object. /// Creates a legacy Spinner-type hit object.

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
public double Distance { get; set; } public double Distance { get; set; }
public List<List<SampleInfo>> RepeatSamples { get; set; } public List<List<SampleInfo>> NodeSamples { get; set; }
public int RepeatCount { get; set; } public int RepeatCount { get; set; }
public double EndTime => StartTime + this.SpanCount() * Distance / Velocity; public double EndTime => StartTime + this.SpanCount() * Distance / Velocity;

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
}; };
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{ {
return new ConvertSlider return new ConvertSlider
{ {
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = length, Distance = length,
CurveType = curveType, CurveType = curveType,
RepeatSamples = repeatSamples, NodeSamples = nodeSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };
} }

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
}; };
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{ {
newCombo |= forceNewCombo; newCombo |= forceNewCombo;
comboOffset += extraComboOffset; comboOffset += extraComboOffset;
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = Math.Max(0, length), Distance = Math.Max(0, length),
CurveType = curveType, CurveType = curveType,
RepeatSamples = repeatSamples, NodeSamples = nodeSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };
} }

View File

@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
return new ConvertHit(); return new ConvertHit();
} }
protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> repeatSamples) protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount, List<List<SampleInfo>> nodeSamples)
{ {
return new ConvertSlider return new ConvertSlider
{ {
ControlPoints = controlPoints, ControlPoints = controlPoints,
Distance = length, Distance = length,
CurveType = curveType, CurveType = curveType,
RepeatSamples = repeatSamples, NodeSamples = nodeSamples,
RepeatCount = repeatCount RepeatCount = repeatCount
}; };
} }

View File

@ -17,9 +17,15 @@ namespace osu.Game.Rulesets.Objects.Types
int RepeatCount { get; } int RepeatCount { get; }
/// <summary> /// <summary>
/// The samples to be played when each repeat node is hit (0 -> first repeat node, 1 -> second repeat node, etc). /// The samples to be played when each node of the <see cref="IHasRepeats"/> is hit.<br />
/// 0: The first node.<br />
/// 1: The first repeat.<br />
/// 2: The second repeat.<br />
/// ...<br />
/// n-1: The last repeat.<br />
/// n: The last node.
/// </summary> /// </summary>
List<List<SampleInfo>> RepeatSamples { get; } List<List<SampleInfo>> NodeSamples { get; }
} }
public static class HasRepeatsExtensions public static class HasRepeatsExtensions