mirror of
https://github.com/ppy/osu.git
synced 2024-12-13 08:32:57 +08:00
Fix slider nodes using the wrong samples
This commit is contained in:
parent
33d4ec876a
commit
47be95ce0b
@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
|
||||
ControlPoints = curveData.ControlPoints,
|
||||
CurveType = curveData.CurveType,
|
||||
Distance = curveData.Distance,
|
||||
RepeatSamples = curveData.RepeatSamples,
|
||||
NodeSamples = curveData.NodeSamples,
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
X = (positionData?.X ?? 0) / CatchPlayfield.BASE_WIDTH,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
||||
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
|
||||
{
|
||||
|
@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount();
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
return curveData.NodeSamples[index];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -470,7 +470,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
return curveData.NodeSamples[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = 700,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats),
|
||||
NodeSamples = createEmptySamples(repeats),
|
||||
StackHeight = 10
|
||||
};
|
||||
|
||||
@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = distance,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats),
|
||||
NodeSamples = createEmptySamples(repeats),
|
||||
StackHeight = stackHeight
|
||||
};
|
||||
|
||||
@ -169,7 +169,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = 600,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = 793.4417,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = 480,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
@ -246,7 +246,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = 1000,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = createEmptySamples(repeats)
|
||||
NodeSamples = createEmptySamples(repeats)
|
||||
};
|
||||
|
||||
addSlider(slider, 2, 3);
|
||||
@ -274,7 +274,7 @@ namespace osu.Game.Rulesets.Osu.Tests
|
||||
},
|
||||
Distance = 300,
|
||||
RepeatCount = repeats,
|
||||
RepeatSamples = repeatSamples
|
||||
NodeSamples = repeatSamples
|
||||
};
|
||||
|
||||
addSlider(slider, 3, 1);
|
||||
|
@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
|
||||
ControlPoints = curveData.ControlPoints,
|
||||
CurveType = curveData.CurveType,
|
||||
Distance = curveData.Distance,
|
||||
RepeatSamples = curveData.RepeatSamples,
|
||||
NodeSamples = curveData.NodeSamples,
|
||||
RepeatCount = curveData.RepeatCount,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false,
|
||||
|
@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
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; }
|
||||
|
||||
/// <summary>
|
||||
@ -129,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
StartTime = StartTime,
|
||||
Position = Position,
|
||||
Samples = Samples,
|
||||
Samples = NodeSamples[0],
|
||||
SampleControlPoint = SampleControlPoint,
|
||||
IndexInCurrentCombo = IndexInCurrentCombo,
|
||||
ComboIndex = ComboIndex,
|
||||
@ -209,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
Position = Position + Curve.PositionAt(repeat % 2),
|
||||
StackHeight = StackHeight,
|
||||
Scale = Scale,
|
||||
Samples = new List<SampleInfo>(RepeatSamples[repeatIndex])
|
||||
Samples = new List<SampleInfo>(NodeSamples[1 + repeatIndex])
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||
|
||||
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;
|
||||
for (double j = obj.StartTime; j <= obj.StartTime + taikoDuration + tickSpacing / 8; j += tickSpacing)
|
||||
|
@ -13,6 +13,7 @@ using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Rulesets.Catch.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Legacy;
|
||||
using osu.Game.Rulesets.Osu.Beatmaps;
|
||||
using osu.Game.Skinning;
|
||||
|
||||
@ -312,5 +313,48 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
23
osu.Game.Tests/Resources/slider-samples.osu
Normal file
23
osu.Game.Tests/Resources/slider-samples.osu
Normal 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
|
@ -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;
|
||||
comboOffset += extraComboOffset;
|
||||
@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatSamples = repeatSamples,
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
@ -169,6 +169,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i]));
|
||||
|
||||
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))
|
||||
{
|
||||
@ -200,7 +203,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@ -260,9 +265,9 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
/// <param name="length">The slider length.</param>
|
||||
/// <param name="curveType">The slider curve type.</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>
|
||||
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>
|
||||
/// Creates a legacy Spinner-type hit object.
|
||||
|
@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
||||
|
||||
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 double EndTime => StartTime + this.SpanCount() * Distance / Velocity;
|
||||
|
@ -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
|
||||
{
|
||||
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Mania
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatSamples = repeatSamples,
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
@ -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;
|
||||
comboOffset += extraComboOffset;
|
||||
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu
|
||||
ControlPoints = controlPoints,
|
||||
Distance = Math.Max(0, length),
|
||||
CurveType = curveType,
|
||||
RepeatSamples = repeatSamples,
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
@ -23,14 +23,14 @@ namespace osu.Game.Rulesets.Objects.Legacy.Taiko
|
||||
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
|
||||
{
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatSamples = repeatSamples,
|
||||
NodeSamples = nodeSamples,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
@ -17,9 +17,15 @@ namespace osu.Game.Rulesets.Objects.Types
|
||||
int RepeatCount { get; }
|
||||
|
||||
/// <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>
|
||||
List<List<SampleInfo>> RepeatSamples { get; }
|
||||
List<List<SampleInfo>> NodeSamples { get; }
|
||||
}
|
||||
|
||||
public static class HasRepeatsExtensions
|
||||
|
Loading…
Reference in New Issue
Block a user