mirror of
https://github.com/ppy/osu.git
synced 2024-11-11 10:07:52 +08:00
Merge pull request #2876 from smoogipoo/more-mania-conversion-fixes
Fix more instances of osu!mania beatmap conversion failing
This commit is contained in:
commit
c194618df3
@ -5,6 +5,8 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.Objects;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -13,11 +15,10 @@ using osu.Game.Tests.Beatmaps;
|
||||
namespace osu.Game.Rulesets.Mania.Tests
|
||||
{
|
||||
[TestFixture]
|
||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
|
||||
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ManiaConvertMapping, ConvertValue>
|
||||
{
|
||||
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
|
||||
|
||||
[NonParallelizable]
|
||||
[TestCase("basic")]
|
||||
public new void Test(string name)
|
||||
{
|
||||
@ -34,9 +35,35 @@ namespace osu.Game.Rulesets.Mania.Tests
|
||||
};
|
||||
}
|
||||
|
||||
protected override ManiaConvertMapping CreateConvertMapping() => new ManiaConvertMapping(Converter);
|
||||
|
||||
protected override Ruleset CreateRuleset() => new ManiaRuleset();
|
||||
}
|
||||
|
||||
public class ManiaConvertMapping : ConvertMapping<ConvertValue>, IEquatable<ManiaConvertMapping>
|
||||
{
|
||||
public uint RandomW;
|
||||
public uint RandomX;
|
||||
public uint RandomY;
|
||||
public uint RandomZ;
|
||||
|
||||
public ManiaConvertMapping()
|
||||
{
|
||||
}
|
||||
|
||||
public ManiaConvertMapping(IBeatmapConverter converter)
|
||||
{
|
||||
var maniaConverter = (ManiaBeatmapConverter)converter;
|
||||
RandomW = maniaConverter.Random.W;
|
||||
RandomX = maniaConverter.Random.X;
|
||||
RandomY = maniaConverter.Random.Y;
|
||||
RandomZ = maniaConverter.Random.Z;
|
||||
}
|
||||
|
||||
public bool Equals(ManiaConvertMapping other) => other != null && RandomW == other.RandomW && RandomX == other.RandomX && RandomY == other.RandomY && RandomZ == other.RandomZ;
|
||||
public override bool Equals(ConvertMapping<ConvertValue> other) => base.Equals(other) && Equals(other as ManiaConvertMapping);
|
||||
}
|
||||
|
||||
public struct ConvertValue : IEquatable<ConvertValue>
|
||||
{
|
||||
/// <summary>
|
||||
|
@ -5,6 +5,7 @@ using osu.Game.Rulesets.Mania.Objects;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.Objects.Types;
|
||||
@ -28,8 +29,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
public int TargetColumns;
|
||||
public readonly bool IsForCurrentRuleset;
|
||||
|
||||
// Internal for testing purposes
|
||||
internal FastRandom Random { get; private set; }
|
||||
|
||||
private Pattern lastPattern = new Pattern();
|
||||
private FastRandom random;
|
||||
|
||||
private ManiaBeatmap beatmap;
|
||||
|
||||
@ -62,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
|
||||
|
||||
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
|
||||
random = new FastRandom(seed);
|
||||
Random = new FastRandom(seed);
|
||||
|
||||
return base.ConvertBeatmap(original);
|
||||
}
|
||||
@ -100,7 +103,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
|
||||
private double lastTime;
|
||||
private Vector2 lastPosition;
|
||||
private PatternType lastStair;
|
||||
private PatternType lastStair = PatternType.Stair;
|
||||
private void recordNote(double time, Vector2 position)
|
||||
{
|
||||
lastTime = time;
|
||||
@ -115,12 +118,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
/// <returns>The hit objects generated.</returns>
|
||||
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap)
|
||||
{
|
||||
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
||||
var generator = new SpecificBeatmapPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
|
||||
|
||||
Pattern newPattern = generator.Generate();
|
||||
lastPattern = newPattern;
|
||||
foreach (var newPattern in generator.Generate())
|
||||
{
|
||||
lastPattern = newPattern;
|
||||
|
||||
return newPattern.HitObjects;
|
||||
foreach (var obj in newPattern.HitObjects)
|
||||
yield return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -138,27 +144,44 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
Patterns.PatternGenerator conversion = null;
|
||||
|
||||
if (distanceData != null)
|
||||
conversion = new DistanceObjectPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
|
||||
{
|
||||
var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap);
|
||||
conversion = generator;
|
||||
|
||||
for (double time = original.StartTime; !Precision.DefinitelyBigger(time, generator.EndTime); time += generator.SegmentDuration)
|
||||
{
|
||||
recordNote(time, positionData?.Position ?? Vector2.Zero);
|
||||
computeDensity(time);
|
||||
}
|
||||
}
|
||||
else if (endTimeData != null)
|
||||
conversion = new EndTimeObjectPatternGenerator(random, original, beatmap, originalBeatmap);
|
||||
{
|
||||
conversion = new EndTimeObjectPatternGenerator(Random, original, beatmap, originalBeatmap);
|
||||
|
||||
recordNote(endTimeData.EndTime, new Vector2(256, 192));
|
||||
computeDensity(endTimeData.EndTime);
|
||||
}
|
||||
else if (positionData != null)
|
||||
{
|
||||
computeDensity(original.StartTime);
|
||||
|
||||
conversion = new HitObjectPatternGenerator(random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
||||
conversion = new HitObjectPatternGenerator(Random, original, beatmap, lastPattern, lastTime, lastPosition, density, lastStair, originalBeatmap);
|
||||
|
||||
recordNote(original.StartTime, positionData.Position);
|
||||
}
|
||||
|
||||
if (conversion == null)
|
||||
return null;
|
||||
yield break;
|
||||
|
||||
Pattern newPattern = conversion.Generate();
|
||||
foreach (var newPattern in conversion.Generate())
|
||||
{
|
||||
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
||||
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
||||
|
||||
lastPattern = conversion is EndTimeObjectPatternGenerator ? lastPattern : newPattern;
|
||||
lastStair = (conversion as HitObjectPatternGenerator)?.StairType ?? lastStair;
|
||||
foreach (var obj in newPattern.HitObjects)
|
||||
yield return obj;
|
||||
|
||||
return newPattern.HitObjects;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -171,7 +194,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
||||
{
|
||||
}
|
||||
|
||||
public override Pattern Generate()
|
||||
public override IEnumerable<Pattern> Generate()
|
||||
{
|
||||
yield return generate();
|
||||
}
|
||||
|
||||
private Pattern generate()
|
||||
{
|
||||
var endTimeData = HitObject as IHasEndTime;
|
||||
var positionData = HitObject as IHasXPosition;
|
||||
|
@ -4,6 +4,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Framework.MathUtils;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Rulesets.Mania.MathUtils;
|
||||
@ -24,8 +25,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
/// </summary>
|
||||
private const float osu_base_scoring_distance = 100;
|
||||
|
||||
private readonly double endTime;
|
||||
private readonly double segmentDuration;
|
||||
public readonly double EndTime;
|
||||
public readonly double SegmentDuration;
|
||||
|
||||
private readonly int spanCount;
|
||||
|
||||
private PatternType convertType;
|
||||
@ -52,53 +54,81 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
// The duration of the osu! hit object
|
||||
double osuDuration = distance / osuVelocity;
|
||||
|
||||
endTime = hitObject.StartTime + osuDuration;
|
||||
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
||||
EndTime = hitObject.StartTime + osuDuration;
|
||||
SegmentDuration = (EndTime - HitObject.StartTime) / spanCount;
|
||||
}
|
||||
|
||||
public override Pattern Generate()
|
||||
public override IEnumerable<Pattern> Generate()
|
||||
{
|
||||
var originalPattern = generate();
|
||||
|
||||
if (originalPattern.HitObjects.Count() == 1)
|
||||
{
|
||||
yield return originalPattern;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// We need to split the intermediate pattern into two new patterns:
|
||||
// 1. A pattern containing all objects that do not end at our EndTime.
|
||||
// 2. A pattern containing all objects that end at our EndTime. This will be used for further pattern generation.
|
||||
var intermediatePattern = new Pattern();
|
||||
var endTimePattern = new Pattern();
|
||||
|
||||
foreach (var obj in originalPattern.HitObjects)
|
||||
{
|
||||
if (!Precision.AlmostEquals(EndTime, (obj as IHasEndTime)?.EndTime ?? obj.StartTime))
|
||||
intermediatePattern.Add(obj);
|
||||
else
|
||||
endTimePattern.Add(obj);
|
||||
}
|
||||
|
||||
yield return intermediatePattern;
|
||||
yield return endTimePattern;
|
||||
}
|
||||
|
||||
private Pattern generate()
|
||||
{
|
||||
if (TotalColumns == 1)
|
||||
{
|
||||
var pattern = new Pattern();
|
||||
addToPattern(pattern, 0, HitObject.StartTime, endTime);
|
||||
addToPattern(pattern, 0, HitObject.StartTime, EndTime);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if (spanCount > 1)
|
||||
{
|
||||
if (segmentDuration <= 90)
|
||||
if (SegmentDuration <= 90)
|
||||
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
||||
|
||||
if (segmentDuration <= 120)
|
||||
if (SegmentDuration <= 120)
|
||||
{
|
||||
convertType |= PatternType.ForceNotStack;
|
||||
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
||||
}
|
||||
|
||||
if (segmentDuration <= 160)
|
||||
if (SegmentDuration <= 160)
|
||||
return generateStair(HitObject.StartTime);
|
||||
|
||||
if (segmentDuration <= 200 && ConversionDifficulty > 3)
|
||||
if (SegmentDuration <= 200 && ConversionDifficulty > 3)
|
||||
return generateRandomMultipleNotes(HitObject.StartTime);
|
||||
|
||||
double duration = endTime - HitObject.StartTime;
|
||||
double duration = EndTime - HitObject.StartTime;
|
||||
if (duration >= 4000)
|
||||
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
||||
|
||||
if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
||||
if (SegmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart)
|
||||
return generateTiledHoldNotes(HitObject.StartTime);
|
||||
|
||||
return generateHoldAndNormalNotes(HitObject.StartTime);
|
||||
}
|
||||
|
||||
if (segmentDuration <= 110)
|
||||
if (SegmentDuration <= 110)
|
||||
{
|
||||
if (PreviousPattern.ColumnWithObjects < TotalColumns)
|
||||
convertType |= PatternType.ForceNotStack;
|
||||
else
|
||||
convertType &= ~PatternType.ForceNotStack;
|
||||
return generateRandomNotes(HitObject.StartTime, segmentDuration < 80 ? 1 : 2);
|
||||
return generateRandomNotes(HitObject.StartTime, SegmentDuration < 80 ? 1 : 2);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 6.5)
|
||||
@ -148,7 +178,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn) || PreviousPattern.ColumnHasObject(nextColumn)) //find available column
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
}
|
||||
|
||||
// This is can't be combined with the above loop due to RNG
|
||||
@ -156,7 +186,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
while (pattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
}
|
||||
|
||||
return pattern;
|
||||
@ -193,7 +223,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
|
||||
lastColumn = nextColumn;
|
||||
startTime += segmentDuration;
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
|
||||
return pattern;
|
||||
@ -223,7 +253,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
for (int i = 0; i <= spanCount; i++)
|
||||
{
|
||||
addToPattern(pattern, column, startTime, startTime);
|
||||
startTime += segmentDuration;
|
||||
startTime += SegmentDuration;
|
||||
|
||||
// Check if we're at the borders of the stage, and invert the pattern if so
|
||||
if (increasing)
|
||||
@ -284,7 +314,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
startTime += segmentDuration;
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
|
||||
return pattern;
|
||||
@ -372,8 +402,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
while (pattern.ColumnHasObject(nextColumn))
|
||||
nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
|
||||
addToPattern(pattern, nextColumn, startTime, endTime);
|
||||
startTime += segmentDuration;
|
||||
addToPattern(pattern, nextColumn, startTime, EndTime);
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
|
||||
return pattern;
|
||||
@ -402,7 +432,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
}
|
||||
|
||||
// Create the hold note
|
||||
addToPattern(pattern, holdColumn, startTime, endTime);
|
||||
addToPattern(pattern, holdColumn, startTime, EndTime);
|
||||
|
||||
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||
int noteCount;
|
||||
@ -434,7 +464,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
pattern.Add(rowPattern);
|
||||
rowPattern.Clear();
|
||||
|
||||
startTime += segmentDuration;
|
||||
startTime += SegmentDuration;
|
||||
}
|
||||
|
||||
return pattern;
|
||||
@ -452,7 +482,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
if (curveData == null)
|
||||
return HitObject.Samples;
|
||||
|
||||
double segmentTime = (endTime - HitObject.StartTime) / spanCount;
|
||||
double segmentTime = (EndTime - HitObject.StartTime) / spanCount;
|
||||
|
||||
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||
return curveData.RepeatSamples[index];
|
||||
|
@ -24,7 +24,12 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
endTime = endtimeData?.EndTime ?? 0;
|
||||
}
|
||||
|
||||
public override Pattern Generate()
|
||||
public override IEnumerable<Pattern> Generate()
|
||||
{
|
||||
yield return generate();
|
||||
}
|
||||
|
||||
private Pattern generate()
|
||||
{
|
||||
var pattern = new Pattern();
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using OpenTK;
|
||||
using osu.Game.Audio;
|
||||
@ -82,127 +83,133 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
{
|
||||
if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_FINISH) && TotalColumns != 8)
|
||||
convertType |= PatternType.Mirror;
|
||||
else
|
||||
else if (HitObject.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP))
|
||||
convertType |= PatternType.Gathered;
|
||||
}
|
||||
}
|
||||
|
||||
public override Pattern Generate()
|
||||
public override IEnumerable<Pattern> Generate()
|
||||
{
|
||||
if (TotalColumns == 1)
|
||||
yield return generate();
|
||||
}
|
||||
|
||||
private Pattern generate()
|
||||
{
|
||||
var pattern = new Pattern();
|
||||
|
||||
try
|
||||
{
|
||||
var pattern = new Pattern();
|
||||
addToPattern(pattern, 0);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||
|
||||
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
||||
{
|
||||
// Generate a new pattern by copying the last hit objects in reverse-column order
|
||||
var pattern = new Pattern();
|
||||
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
if (PreviousPattern.ColumnHasObject(i))
|
||||
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
||||
// If we convert to 7K + 1, let's not overload the special key
|
||||
&& (TotalColumns != 8 || lastColumn != 0)
|
||||
// Make sure the last column was not the centre column
|
||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||
{
|
||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||
var pattern = new Pattern();
|
||||
|
||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||
addToPattern(pattern, column);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
||||
{
|
||||
// Generate a new pattern by placing on the already filled columns
|
||||
var pattern = new Pattern();
|
||||
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
if (PreviousPattern.ColumnHasObject(i))
|
||||
addToPattern(pattern, i);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.Stair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
||||
{
|
||||
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
||||
var pattern = new Pattern();
|
||||
|
||||
int targetColumn = lastColumn + 1;
|
||||
if (targetColumn == TotalColumns)
|
||||
if (TotalColumns == 1)
|
||||
{
|
||||
targetColumn = RandomStart;
|
||||
StairType = PatternType.ReverseStair;
|
||||
addToPattern(pattern, 0);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
addToPattern(pattern, targetColumn);
|
||||
return pattern;
|
||||
}
|
||||
int lastColumn = PreviousPattern.HitObjects.FirstOrDefault()?.Column ?? 0;
|
||||
|
||||
if ((convertType & PatternType.ReverseStair) > 0 && PreviousPattern.HitObjects.Count() == 1)
|
||||
{
|
||||
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
||||
var pattern = new Pattern();
|
||||
|
||||
int targetColumn = lastColumn - 1;
|
||||
if (targetColumn == RandomStart - 1)
|
||||
if ((convertType & PatternType.Reverse) > 0 && PreviousPattern.HitObjects.Any())
|
||||
{
|
||||
targetColumn = TotalColumns - 1;
|
||||
StairType = PatternType.Stair;
|
||||
// Generate a new pattern by copying the last hit objects in reverse-column order
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
if (PreviousPattern.ColumnHasObject(i))
|
||||
addToPattern(pattern, RandomStart + TotalColumns - i - 1);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
addToPattern(pattern, targetColumn);
|
||||
return pattern;
|
||||
}
|
||||
if ((convertType & PatternType.Cycle) > 0 && PreviousPattern.HitObjects.Count() == 1
|
||||
// If we convert to 7K + 1, let's not overload the special key
|
||||
&& (TotalColumns != 8 || lastColumn != 0)
|
||||
// Make sure the last column was not the centre column
|
||||
&& (TotalColumns % 2 == 0 || lastColumn != TotalColumns / 2))
|
||||
{
|
||||
// Generate a new pattern by cycling backwards (similar to Reverse but for only one hit object)
|
||||
int column = RandomStart + TotalColumns - lastColumn - 1;
|
||||
addToPattern(pattern, column);
|
||||
|
||||
if ((convertType & PatternType.KeepSingle) > 0)
|
||||
return generateRandomNotes(1);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.ForceStack) > 0 && PreviousPattern.HitObjects.Any())
|
||||
{
|
||||
// Generate a new pattern by placing on the already filled columns
|
||||
for (int i = RandomStart; i < TotalColumns; i++)
|
||||
if (PreviousPattern.ColumnHasObject(i))
|
||||
addToPattern(pattern, i);
|
||||
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if (PreviousPattern.HitObjects.Count() == 1)
|
||||
{
|
||||
if ((convertType & PatternType.Stair) > 0)
|
||||
{
|
||||
// Generate a new pattern by placing on the next column, cycling back to the start if there is no "next"
|
||||
int targetColumn = lastColumn + 1;
|
||||
if (targetColumn == TotalColumns)
|
||||
targetColumn = RandomStart;
|
||||
|
||||
addToPattern(pattern, targetColumn);
|
||||
return pattern;
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.ReverseStair) > 0)
|
||||
{
|
||||
// Generate a new pattern by placing on the previous column, cycling back to the end if there is no "previous"
|
||||
int targetColumn = lastColumn - 1;
|
||||
if (targetColumn == RandomStart - 1)
|
||||
targetColumn = TotalColumns - 1;
|
||||
|
||||
addToPattern(pattern, targetColumn);
|
||||
return pattern;
|
||||
}
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.KeepSingle) > 0)
|
||||
return pattern = generateRandomNotes(1);
|
||||
|
||||
if ((convertType & PatternType.Mirror) > 0)
|
||||
{
|
||||
if (ConversionDifficulty > 6.5)
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||
if (ConversionDifficulty > 4)
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
||||
return pattern = generateRandomPatternWithMirrored(0.12, 0, 0);
|
||||
}
|
||||
|
||||
if ((convertType & PatternType.Mirror) > 0)
|
||||
{
|
||||
if (ConversionDifficulty > 6.5)
|
||||
return generateRandomPatternWithMirrored(0.12, 0.38, 0.12);
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
return pattern = generateRandomPattern(0.78, 0.42, 0, 0);
|
||||
return pattern = generateRandomPattern(1, 0.62, 0, 0);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 4)
|
||||
return generateRandomPatternWithMirrored(0.12, 0.17, 0);
|
||||
return generateRandomPatternWithMirrored(0.12, 0, 0);
|
||||
}
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
return pattern = generateRandomPattern(0.35, 0.08, 0, 0);
|
||||
return pattern = generateRandomPattern(0.52, 0.15, 0, 0);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 6.5)
|
||||
if (ConversionDifficulty > 2)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
return pattern = generateRandomPattern(0.18, 0, 0, 0);
|
||||
return pattern = generateRandomPattern(0.45, 0, 0, 0);
|
||||
}
|
||||
|
||||
return pattern = generateRandomPattern(0, 0, 0, 0);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
return generateRandomPattern(0.78, 0.42, 0, 0);
|
||||
return generateRandomPattern(1, 0.62, 0, 0);
|
||||
foreach (var obj in pattern.HitObjects)
|
||||
{
|
||||
if ((convertType & PatternType.Stair) > 0 && obj.Column == TotalColumns - 1)
|
||||
StairType = PatternType.ReverseStair;
|
||||
if ((convertType & PatternType.ReverseStair) > 0 && obj.Column == RandomStart)
|
||||
StairType = PatternType.Stair;
|
||||
}
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 4)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
return generateRandomPattern(0.35, 0.08, 0, 0);
|
||||
return generateRandomPattern(0.52, 0.15, 0, 0);
|
||||
}
|
||||
|
||||
if (ConversionDifficulty > 2)
|
||||
{
|
||||
if ((convertType & PatternType.LowProbability) > 0)
|
||||
return generateRandomPattern(0.18, 0, 0, 0);
|
||||
return generateRandomPattern(0.45, 0, 0, 0);
|
||||
}
|
||||
|
||||
return generateRandomPattern(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -103,17 +103,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
||||
HitObject lastObject = OriginalBeatmap.HitObjects.LastOrDefault();
|
||||
HitObject firstObject = OriginalBeatmap.HitObjects.FirstOrDefault();
|
||||
|
||||
double drainTime = (lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0);
|
||||
drainTime -= OriginalBeatmap.TotalBreakTime;
|
||||
// Drain time in seconds
|
||||
int drainTime = (int)(((lastObject?.StartTime ?? 0) - (firstObject?.StartTime ?? 0) - OriginalBeatmap.TotalBreakTime) / 1000);
|
||||
|
||||
if (drainTime == 0)
|
||||
drainTime = 10000000;
|
||||
|
||||
// We need this in seconds
|
||||
drainTime /= 1000;
|
||||
drainTime = 10000;
|
||||
|
||||
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
|
||||
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
|
||||
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + (double)OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
|
||||
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
|
||||
|
||||
return conversionDifficulty.Value;
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
|
||||
namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
@ -42,9 +43,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generates the pattern for <see cref="HitObject"/>, filled with hit objects.
|
||||
/// Generates the patterns for <see cref="HitObject"/>, each filled with hit objects.
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="Pattern"/> containing the hit objects.</returns>
|
||||
public abstract Pattern Generate();
|
||||
/// <returns>The <see cref="Pattern"/>s containing the hit objects.</returns>
|
||||
public abstract IEnumerable<Pattern> Generate();
|
||||
}
|
||||
}
|
||||
|
@ -15,11 +15,15 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
private const uint y = 842502087;
|
||||
private const uint z = 3579807591;
|
||||
private const uint w = 273326509;
|
||||
private uint _x, _y = y, _z = z, _w = w;
|
||||
|
||||
internal uint X { get; private set; }
|
||||
internal uint Y { get; private set; } = y;
|
||||
internal uint Z { get; private set; } = z;
|
||||
internal uint W { get; private set; } = w;
|
||||
|
||||
public FastRandom(int seed)
|
||||
{
|
||||
_x = (uint)seed;
|
||||
X = (uint)seed;
|
||||
}
|
||||
|
||||
public FastRandom()
|
||||
@ -33,11 +37,11 @@ namespace osu.Game.Rulesets.Mania.MathUtils
|
||||
/// <returns>The random value.</returns>
|
||||
public uint NextUInt()
|
||||
{
|
||||
uint t = _x ^ _x << 11;
|
||||
_x = _y;
|
||||
_y = _z;
|
||||
_z = _w;
|
||||
return _w = _w ^ _w >> 19 ^ t ^ t >> 8;
|
||||
uint t = X ^ X << 11;
|
||||
X = Y;
|
||||
Y = Z;
|
||||
Z = W;
|
||||
return W = W ^ W >> 19 ^ t ^ t >> 8;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -1,103 +1,132 @@
|
||||
{
|
||||
"Mappings": [{
|
||||
"StartTime": 500,
|
||||
"Objects": [{
|
||||
"StartTime": 500,
|
||||
"EndTime": 2500,
|
||||
"Column": 0
|
||||
},
|
||||
{
|
||||
"StartTime": 1500,
|
||||
"EndTime": 2500,
|
||||
"Column": 1
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"StartTime": 3000,
|
||||
"Objects": [{
|
||||
"StartTime": 3000,
|
||||
"EndTime": 4000,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 4500,
|
||||
"Objects": [{
|
||||
"StartTime": 4500,
|
||||
"EndTime": 5500,
|
||||
"Column": 4
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 6000,
|
||||
"Objects": [{
|
||||
"StartTime": 6000,
|
||||
"EndTime": 6500,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 7000,
|
||||
"Objects": [{
|
||||
"StartTime": 7000,
|
||||
"EndTime": 8000,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 8500,
|
||||
"Objects": [{
|
||||
"StartTime": 8500,
|
||||
"EndTime": 11000,
|
||||
"Column": 0
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 11500,
|
||||
"Objects": [{
|
||||
"StartTime": 11500,
|
||||
"EndTime": 12000,
|
||||
"Column": 1
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 12500,
|
||||
"Objects": [{
|
||||
"StartTime": 12500,
|
||||
"EndTime": 16500,
|
||||
"Column": 4
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 17000,
|
||||
"Objects": [{
|
||||
"StartTime": 17000,
|
||||
"EndTime": 18000,
|
||||
"Column": 2
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 18500,
|
||||
"Objects": [{
|
||||
"StartTime": 18500,
|
||||
"EndTime": 19450,
|
||||
"Column": 0
|
||||
}]
|
||||
},
|
||||
{
|
||||
"StartTime": 19875,
|
||||
"Objects": [{
|
||||
"StartTime": 19875,
|
||||
"EndTime": 23875,
|
||||
"Column": 1
|
||||
},
|
||||
{
|
||||
"StartTime": 19875,
|
||||
"EndTime": 23875,
|
||||
"Column": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
"RandomW": 2659373485,
|
||||
"RandomX": 3579807591,
|
||||
"RandomY": 273326509,
|
||||
"RandomZ": 272969173,
|
||||
"StartTime": 500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 500.0,
|
||||
"EndTime": 2500.0,
|
||||
"Column": 0
|
||||
}, {
|
||||
"StartTime": 1500.0,
|
||||
"EndTime": 2500.0,
|
||||
"Column": 1
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 3083803045,
|
||||
"RandomX": 273326509,
|
||||
"RandomY": 272969173,
|
||||
"RandomZ": 2659373485,
|
||||
"StartTime": 3000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 3000.0,
|
||||
"EndTime": 4000.0,
|
||||
"Column": 2
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 4073554232,
|
||||
"RandomX": 272969173,
|
||||
"RandomY": 2659373485,
|
||||
"RandomZ": 3083803045,
|
||||
"StartTime": 4500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 4500.0,
|
||||
"EndTime": 5500.0,
|
||||
"Column": 4
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 3420401969,
|
||||
"RandomX": 2659373485,
|
||||
"RandomY": 3083803045,
|
||||
"RandomZ": 4073554232,
|
||||
"StartTime": 6000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 6000.0,
|
||||
"EndTime": 6500.0,
|
||||
"Column": 2
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 1129881182,
|
||||
"RandomX": 3083803045,
|
||||
"RandomY": 4073554232,
|
||||
"RandomZ": 3420401969,
|
||||
"StartTime": 7000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 7000.0,
|
||||
"EndTime": 8000.0,
|
||||
"Column": 2
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 315568458,
|
||||
"RandomX": 3420401969,
|
||||
"RandomY": 1129881182,
|
||||
"RandomZ": 2358617505,
|
||||
"StartTime": 8500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 8500.0,
|
||||
"EndTime": 11000.0,
|
||||
"Column": 0
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 548134043,
|
||||
"RandomX": 1129881182,
|
||||
"RandomY": 2358617505,
|
||||
"RandomZ": 315568458,
|
||||
"StartTime": 11500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 11500.0,
|
||||
"EndTime": 12000.0,
|
||||
"Column": 1
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 3979422122,
|
||||
"RandomX": 548134043,
|
||||
"RandomY": 2810584254,
|
||||
"RandomZ": 2250186050,
|
||||
"StartTime": 12500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 12500.0,
|
||||
"EndTime": 16500.0,
|
||||
"Column": 4
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 2466283411,
|
||||
"RandomX": 2810584254,
|
||||
"RandomY": 2250186050,
|
||||
"RandomZ": 3979422122,
|
||||
"StartTime": 17000.0,
|
||||
"Objects": [{
|
||||
"StartTime": 17000.0,
|
||||
"EndTime": 18000.0,
|
||||
"Column": 2
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 83157665,
|
||||
"RandomX": 2250186050,
|
||||
"RandomY": 3979422122,
|
||||
"RandomZ": 2466283411,
|
||||
"StartTime": 18500.0,
|
||||
"Objects": [{
|
||||
"StartTime": 18500.0,
|
||||
"EndTime": 19450.0,
|
||||
"Column": 0
|
||||
}]
|
||||
}, {
|
||||
"RandomW": 2383087700,
|
||||
"RandomX": 83157665,
|
||||
"RandomY": 2055150192,
|
||||
"RandomZ": 510071020,
|
||||
"StartTime": 19875.0,
|
||||
"Objects": [{
|
||||
"StartTime": 19875.0,
|
||||
"EndTime": 23875.0,
|
||||
"Column": 1
|
||||
}, {
|
||||
"StartTime": 19875.0,
|
||||
"EndTime": 23875.0,
|
||||
"Column": 0
|
||||
}]
|
||||
}]
|
||||
}
|
@ -25,6 +25,8 @@ namespace osu.Game.Tests.Beatmaps
|
||||
|
||||
protected abstract string ResourceAssembly { get; }
|
||||
|
||||
protected IBeatmapConverter Converter { get; private set; }
|
||||
|
||||
protected void Test(string name)
|
||||
{
|
||||
var ourResult = convert(name);
|
||||
@ -41,14 +43,22 @@ namespace osu.Game.Tests.Beatmaps
|
||||
Assert.Fail($"A conversion did not generate any hitobjects, but should have, for hitobject at time: {expectedResult.Mappings[mappingCounter].StartTime}\n");
|
||||
else if (mappingCounter >= expectedResult.Mappings.Count)
|
||||
Assert.Fail($"A conversion generated hitobjects, but should not have, for hitobject at time: {ourResult.Mappings[mappingCounter].StartTime}\n");
|
||||
else if (!expectedResult.Mappings[mappingCounter].Equals(ourResult.Mappings[mappingCounter]))
|
||||
{
|
||||
var expectedMapping = expectedResult.Mappings[mappingCounter];
|
||||
var ourMapping = ourResult.Mappings[mappingCounter];
|
||||
|
||||
Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
|
||||
+ $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
|
||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
var counter = mappingCounter;
|
||||
var ourMapping = ourResult.Mappings[mappingCounter];
|
||||
var expectedMapping = expectedResult.Mappings[mappingCounter];
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
var ourMapping = ourResult.Mappings[counter];
|
||||
var expectedMapping = expectedResult.Mappings[counter];
|
||||
|
||||
int objectCounter = 0;
|
||||
while (true)
|
||||
{
|
||||
@ -60,10 +70,6 @@ namespace osu.Game.Tests.Beatmaps
|
||||
else if (objectCounter >= expectedMapping.Objects.Count)
|
||||
Assert.Fail($"The conversion generated a hitobject, but should not have, for hitobject at time: {ourMapping.StartTime}:\n"
|
||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping.Objects[objectCounter])}\n");
|
||||
else if (!expectedMapping.Equals(ourMapping))
|
||||
Assert.Fail($"The conversion mapping differed for object at time {expectedMapping.StartTime}:\n"
|
||||
+ $"Expected {JsonConvert.SerializeObject(expectedMapping)}\n"
|
||||
+ $"Received: {JsonConvert.SerializeObject(ourMapping)}\n");
|
||||
else if (!expectedMapping.Objects[objectCounter].Equals(ourMapping.Objects[objectCounter]))
|
||||
{
|
||||
Assert.Fail($"The conversion generated differing hitobjects for object at time: {expectedMapping.StartTime}:\n"
|
||||
@ -88,10 +94,11 @@ namespace osu.Game.Tests.Beatmaps
|
||||
var rulesetInstance = CreateRuleset();
|
||||
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
|
||||
|
||||
var result = new ConvertResult();
|
||||
var converter = rulesetInstance.CreateBeatmapConverter(beatmap);
|
||||
Converter = rulesetInstance.CreateBeatmapConverter(beatmap);
|
||||
|
||||
converter.ObjectConverted += (orig, converted) =>
|
||||
var result = new ConvertResult();
|
||||
|
||||
Converter.ObjectConverted += (orig, converted) =>
|
||||
{
|
||||
converted.ForEach(h => h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty));
|
||||
|
||||
@ -103,7 +110,7 @@ namespace osu.Game.Tests.Beatmaps
|
||||
result.Mappings.Add(mapping);
|
||||
};
|
||||
|
||||
IBeatmap convertedBeatmap = converter.Convert();
|
||||
IBeatmap convertedBeatmap = Converter.Convert();
|
||||
rulesetInstance.CreateBeatmapProcessor(convertedBeatmap)?.PostProcess();
|
||||
|
||||
return result;
|
||||
|
Loading…
Reference in New Issue
Block a user