mirror of
https://github.com/osukey/osukey.git
synced 2025-08-03 14:46:38 +09:00
Merge pull request #642 from smoogipooo/better-conversion
Better conversion
This commit is contained in:
@ -45,7 +45,7 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
objects.Add(new HitCircle
|
||||
{
|
||||
StartTime = time,
|
||||
Position = new Vector2(RNG.Next(0, 512), RNG.Next(0, 384)),
|
||||
Position = new Vector2(RNG.Next(0, (int)OsuPlayfield.BASE_SIZE.X), RNG.Next(0, (int)OsuPlayfield.BASE_SIZE.Y)),
|
||||
Scale = RNG.NextSingle(0.5f, 1.0f),
|
||||
});
|
||||
|
||||
|
@ -14,6 +14,7 @@ using osu.Game.Modes.Osu.Objects;
|
||||
using osu.Game.Screens.Play;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Desktop.VisualTests.Beatmaps;
|
||||
using osu.Game.Modes.Osu.UI;
|
||||
|
||||
namespace osu.Desktop.VisualTests.Tests
|
||||
{
|
||||
@ -52,8 +53,8 @@ namespace osu.Desktop.VisualTests.Tests
|
||||
objects.Add(new HitCircle
|
||||
{
|
||||
StartTime = time,
|
||||
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : 512,
|
||||
i % 4 < 2 ? 0 : 384),
|
||||
Position = new Vector2(i % 4 == 0 || i % 4 == 2 ? 0 : OsuPlayfield.BASE_SIZE.X,
|
||||
i % 4 < 2 ? 0 : OsuPlayfield.BASE_SIZE.Y),
|
||||
NewCombo = i % 4 == 0
|
||||
});
|
||||
|
||||
|
@ -4,17 +4,20 @@
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Catch.Objects;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Objects;
|
||||
|
||||
namespace osu.Game.Modes.Catch.Beatmaps
|
||||
{
|
||||
internal class CatchBeatmapConverter : IBeatmapConverter<CatchBaseHit>
|
||||
internal class CatchBeatmapConverter : BeatmapConverter<CatchBaseHit>
|
||||
{
|
||||
public Beatmap<CatchBaseHit> Convert(Beatmap original)
|
||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||
|
||||
protected override IEnumerable<CatchBaseHit> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||
{
|
||||
return new Beatmap<CatchBaseHit>(original)
|
||||
{
|
||||
HitObjects = new List<CatchBaseHit>() // Todo: Convert HitObjects
|
||||
};
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Catch.Objects;
|
||||
|
||||
namespace osu.Game.Modes.Catch.Beatmaps
|
||||
{
|
||||
internal class CatchBeatmapProcessor : IBeatmapProcessor<CatchBaseHit>
|
||||
{
|
||||
public void SetDefaults(CatchBaseHit hitObject, Beatmap<CatchBaseHit> beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostProcess(Beatmap<CatchBaseHit> beatmap)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Catch.Beatmaps;
|
||||
using osu.Game.Modes.Catch.Objects;
|
||||
using System.Collections.Generic;
|
||||
@ -19,6 +20,6 @@ namespace osu.Game.Modes.Catch
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Catch.Beatmaps;
|
||||
using osu.Game.Modes.Catch.Judgements;
|
||||
using osu.Game.Modes.Catch.Objects;
|
||||
@ -21,9 +22,7 @@ namespace osu.Game.Modes.Catch.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(this);
|
||||
|
||||
protected override IBeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
|
||||
protected override IBeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
|
||||
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
|
||||
|
||||
protected override Playfield<CatchBaseHit, CatchJudgement> CreatePlayfield() => new CatchPlayfield();
|
||||
|
||||
|
@ -48,7 +48,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\CatchBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\CatchBeatmapProcessor.cs" />
|
||||
<Compile Include="CatchDifficultyCalculator.cs" />
|
||||
<Compile Include="Scoring\CatchScoreProcessor.cs" />
|
||||
<Compile Include="Judgements\CatchJudgement.cs" />
|
||||
|
@ -4,17 +4,20 @@
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Mania.Objects;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Objects;
|
||||
|
||||
namespace osu.Game.Modes.Mania.Beatmaps
|
||||
{
|
||||
internal class ManiaBeatmapConverter : IBeatmapConverter<ManiaBaseHit>
|
||||
internal class ManiaBeatmapConverter : BeatmapConverter<ManiaBaseHit>
|
||||
{
|
||||
public Beatmap<ManiaBaseHit> Convert(Beatmap original)
|
||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
|
||||
|
||||
protected override IEnumerable<ManiaBaseHit> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||
{
|
||||
return new Beatmap<ManiaBaseHit>(original)
|
||||
{
|
||||
HitObjects = new List<ManiaBaseHit>() // Todo: Implement
|
||||
};
|
||||
yield return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Mania.Objects;
|
||||
|
||||
namespace osu.Game.Modes.Mania.Beatmaps
|
||||
{
|
||||
internal class ManiaBeatmapProcessor : IBeatmapProcessor<ManiaBaseHit>
|
||||
{
|
||||
public void SetDefaults(ManiaBaseHit hitObject, Beatmap<ManiaBaseHit> beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostProcess(Beatmap<ManiaBaseHit> beatmap)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Mania.Beatmaps;
|
||||
using osu.Game.Modes.Mania.Objects;
|
||||
using System.Collections.Generic;
|
||||
@ -20,6 +21,6 @@ namespace osu.Game.Modes.Mania
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
protected override BeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Mania.Beatmaps;
|
||||
using osu.Game.Modes.Mania.Judgements;
|
||||
using osu.Game.Modes.Mania.Objects;
|
||||
@ -24,9 +25,7 @@ namespace osu.Game.Modes.Mania.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new ManiaScoreProcessor(this);
|
||||
|
||||
protected override IBeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
|
||||
protected override IBeatmapProcessor<ManiaBaseHit> CreateBeatmapProcessor() => new ManiaBeatmapProcessor();
|
||||
protected override BeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
|
||||
|
||||
protected override Playfield<ManiaBaseHit, ManiaJudgement> CreatePlayfield() => new ManiaPlayfield(columns);
|
||||
|
||||
|
@ -48,7 +48,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\ManiaBeatmapProcessor.cs" />
|
||||
<Compile Include="Judgements\ManiaJudgement.cs" />
|
||||
<Compile Include="ManiaDifficultyCalculator.cs" />
|
||||
<Compile Include="Scoring\ManiaScoreProcessor.cs" />
|
||||
|
@ -5,33 +5,19 @@ using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Objects;
|
||||
using osu.Game.Modes.Osu.Objects;
|
||||
using osu.Game.Modes.Osu.Objects.Drawables;
|
||||
using System.Collections.Generic;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using osu.Game.Modes.Osu.UI;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Beatmaps
|
||||
{
|
||||
internal class OsuBeatmapConverter : IBeatmapConverter<OsuHitObject>
|
||||
internal class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
|
||||
{
|
||||
public Beatmap<OsuHitObject> Convert(Beatmap original)
|
||||
{
|
||||
return new Beatmap<OsuHitObject>(original)
|
||||
{
|
||||
HitObjects = convertHitObjects(original.HitObjects, original.BeatmapInfo?.StackLeniency ?? 0.7f)
|
||||
};
|
||||
}
|
||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) };
|
||||
|
||||
private List<OsuHitObject> convertHitObjects(List<HitObject> hitObjects, float stackLeniency)
|
||||
{
|
||||
List<OsuHitObject> converted = hitObjects.Select(convertHitObject).ToList();
|
||||
|
||||
updateStacking(converted, stackLeniency);
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
private OsuHitObject convertHitObject(HitObject original)
|
||||
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, Beatmap beatmap)
|
||||
{
|
||||
IHasCurve curveData = original as IHasCurve;
|
||||
IHasEndTime endTimeData = original as IHasEndTime;
|
||||
@ -40,7 +26,7 @@ namespace osu.Game.Modes.Osu.Beatmaps
|
||||
|
||||
if (curveData != null)
|
||||
{
|
||||
return new Slider
|
||||
yield return new Slider
|
||||
{
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
@ -49,172 +35,26 @@ namespace osu.Game.Modes.Osu.Beatmaps
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
};
|
||||
}
|
||||
|
||||
if (endTimeData != null)
|
||||
else if (endTimeData != null)
|
||||
{
|
||||
return new Spinner
|
||||
yield return new Spinner
|
||||
{
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
Position = new Vector2(512, 384) / 2,
|
||||
EndTime = endTimeData.EndTime
|
||||
EndTime = endTimeData.EndTime,
|
||||
|
||||
Position = positionData?.Position ?? OsuPlayfield.BASE_SIZE / 2,
|
||||
};
|
||||
}
|
||||
|
||||
return new HitCircle
|
||||
else
|
||||
{
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
};
|
||||
}
|
||||
|
||||
private void updateStacking(List<OsuHitObject> hitObjects, float stackLeniency, int startIndex = 0, int endIndex = -1)
|
||||
{
|
||||
if (endIndex == -1)
|
||||
endIndex = hitObjects.Count - 1;
|
||||
|
||||
const int stack_distance = 3;
|
||||
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * stackLeniency;
|
||||
|
||||
// Reset stacking inside the update range
|
||||
for (int i = startIndex; i <= endIndex; i++)
|
||||
hitObjects[i].StackHeight = 0;
|
||||
|
||||
// Extend the end index to include objects they are stacked on
|
||||
int extendedEndIndex = endIndex;
|
||||
for (int i = endIndex; i >= startIndex; i--)
|
||||
{
|
||||
int stackBaseIndex = i;
|
||||
for (int n = stackBaseIndex + 1; n < hitObjects.Count; n++)
|
||||
yield return new HitCircle
|
||||
{
|
||||
OsuHitObject stackBaseObject = hitObjects[stackBaseIndex];
|
||||
if (stackBaseObject is Spinner) break;
|
||||
|
||||
OsuHitObject objectN = hitObjects[n];
|
||||
if (objectN is Spinner)
|
||||
continue;
|
||||
|
||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the next object.
|
||||
break;
|
||||
|
||||
if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
|
||||
stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
|
||||
{
|
||||
stackBaseIndex = n;
|
||||
|
||||
// HitObjects after the specified update range haven't been reset yet
|
||||
objectN.StackHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (stackBaseIndex > extendedEndIndex)
|
||||
{
|
||||
extendedEndIndex = stackBaseIndex;
|
||||
if (extendedEndIndex == hitObjects.Count - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Reverse pass for stack calculation.
|
||||
int extendedStartIndex = startIndex;
|
||||
for (int i = extendedEndIndex; i > startIndex; i--)
|
||||
{
|
||||
int n = i;
|
||||
/* We should check every note which has not yet got a stack.
|
||||
* Consider the case we have two interwound stacks and this will make sense.
|
||||
*
|
||||
* o <-1 o <-2
|
||||
* o <-3 o <-4
|
||||
*
|
||||
* We first process starting from 4 and handle 2,
|
||||
* then we come backwards on the i loop iteration until we reach 3 and handle 1.
|
||||
* 2 and 1 will be ignored in the i loop because they already have a stack value.
|
||||
*/
|
||||
|
||||
OsuHitObject objectI = hitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
* Any other case is handled by the "is Slider" code below this.
|
||||
*/
|
||||
if (objectI is HitCircle)
|
||||
{
|
||||
while (--n >= 0)
|
||||
{
|
||||
OsuHitObject objectN = hitObjects[n];
|
||||
if (objectN is Spinner) continue;
|
||||
|
||||
double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime;
|
||||
|
||||
if (objectI.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the previous object.
|
||||
break;
|
||||
|
||||
// HitObjects before the specified update range haven't been reset yet
|
||||
if (n < extendedStartIndex)
|
||||
{
|
||||
objectN.StackHeight = 0;
|
||||
extendedStartIndex = n;
|
||||
}
|
||||
|
||||
/* This is a special case where hticircles are moved DOWN and RIGHT (negative stacking) if they are under the *last* slider in a stacked pattern.
|
||||
* o==o <- slider is at original location
|
||||
* o <- hitCircle has stack of -1
|
||||
* o <- hitCircle has stack of -2
|
||||
*/
|
||||
if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
|
||||
{
|
||||
int offset = objectI.StackHeight - objectN.StackHeight + 1;
|
||||
for (int j = n + 1; j <= i; j++)
|
||||
{
|
||||
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
|
||||
OsuHitObject objectJ = hitObjects[j];
|
||||
if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
|
||||
objectJ.StackHeight -= offset;
|
||||
}
|
||||
|
||||
//We have hit a slider. We should restart calculation using this as the new base.
|
||||
//Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop.
|
||||
break;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
|
||||
{
|
||||
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
|
||||
//NOTE: Sliders with start positions stacking are a special case that is also handled here.
|
||||
|
||||
objectN.StackHeight = objectI.StackHeight + 1;
|
||||
objectI = objectN;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objectI is Slider)
|
||||
{
|
||||
/* We have hit the first slider in a possible stack.
|
||||
* From this point on, we ALWAYS stack positive regardless.
|
||||
*/
|
||||
while (--n >= startIndex)
|
||||
{
|
||||
OsuHitObject objectN = hitObjects[n];
|
||||
if (objectN is Spinner) continue;
|
||||
|
||||
if (objectI.StartTime - objectN.StartTime > stackThreshold)
|
||||
//We are no longer within stacking range of the previous object.
|
||||
break;
|
||||
|
||||
if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
|
||||
{
|
||||
objectN.StackHeight = objectI.StackHeight + 1;
|
||||
objectI = objectN;
|
||||
}
|
||||
}
|
||||
}
|
||||
StartTime = original.StartTime,
|
||||
Samples = original.Samples,
|
||||
Position = positionData?.Position ?? Vector2.Zero,
|
||||
NewCombo = comboData?.NewCombo ?? false
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,21 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using osu.Game.Modes.Osu.Objects;
|
||||
using osu.Game.Modes.Osu.Objects.Drawables;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Beatmaps
|
||||
{
|
||||
internal class OsuBeatmapProcessor : IBeatmapProcessor<OsuHitObject>
|
||||
internal class OsuBeatmapProcessor : BeatmapProcessor<OsuHitObject>
|
||||
{
|
||||
public void PostProcess(Beatmap<OsuHitObject> beatmap)
|
||||
public override void PostProcess(Beatmap<OsuHitObject> beatmap)
|
||||
{
|
||||
applyStacking(beatmap);
|
||||
|
||||
if (beatmap.ComboColors.Count == 0)
|
||||
return;
|
||||
|
||||
@ -28,5 +34,150 @@ namespace osu.Game.Modes.Osu.Beatmaps
|
||||
obj.ComboColour = beatmap.ComboColors[colourIndex];
|
||||
}
|
||||
}
|
||||
|
||||
private void applyStacking(Beatmap<OsuHitObject> beatmap)
|
||||
{
|
||||
const int stack_distance = 3;
|
||||
float stackThreshold = DrawableOsuHitObject.TIME_PREEMPT * beatmap.BeatmapInfo?.StackLeniency ?? 0.7f;
|
||||
|
||||
// Reset stacking
|
||||
for (int i = 0; i <= beatmap.HitObjects.Count - 1; i++)
|
||||
beatmap.HitObjects[i].StackHeight = 0;
|
||||
|
||||
// Extend the end index to include objects they are stacked on
|
||||
int extendedEndIndex = beatmap.HitObjects.Count - 1;
|
||||
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
|
||||
{
|
||||
int stackBaseIndex = i;
|
||||
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
|
||||
{
|
||||
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
|
||||
if (stackBaseObject is Spinner) break;
|
||||
|
||||
OsuHitObject objectN = beatmap.HitObjects[n];
|
||||
if (objectN is Spinner)
|
||||
continue;
|
||||
|
||||
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
|
||||
|
||||
if (objectN.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the next object.
|
||||
break;
|
||||
|
||||
if (Vector2.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
|
||||
stackBaseObject is Slider && Vector2.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
|
||||
{
|
||||
stackBaseIndex = n;
|
||||
|
||||
// HitObjects after the specified update range haven't been reset yet
|
||||
objectN.StackHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (stackBaseIndex > extendedEndIndex)
|
||||
{
|
||||
extendedEndIndex = stackBaseIndex;
|
||||
if (extendedEndIndex == beatmap.HitObjects.Count - 1)
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Reverse pass for stack calculation.
|
||||
int extendedStartIndex = 0;
|
||||
for (int i = extendedEndIndex; i > 0; i--)
|
||||
{
|
||||
int n = i;
|
||||
/* We should check every note which has not yet got a stack.
|
||||
* Consider the case we have two interwound stacks and this will make sense.
|
||||
*
|
||||
* o <-1 o <-2
|
||||
* o <-3 o <-4
|
||||
*
|
||||
* We first process starting from 4 and handle 2,
|
||||
* then we come backwards on the i loop iteration until we reach 3 and handle 1.
|
||||
* 2 and 1 will be ignored in the i loop because they already have a stack value.
|
||||
*/
|
||||
|
||||
OsuHitObject objectI = beatmap.HitObjects[i];
|
||||
if (objectI.StackHeight != 0 || objectI is Spinner) continue;
|
||||
|
||||
/* If this object is a hitcircle, then we enter this "special" case.
|
||||
* It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider.
|
||||
* Any other case is handled by the "is Slider" code below this.
|
||||
*/
|
||||
if (objectI is HitCircle)
|
||||
{
|
||||
while (--n >= 0)
|
||||
{
|
||||
OsuHitObject objectN = beatmap.HitObjects[n];
|
||||
if (objectN is Spinner) continue;
|
||||
|
||||
double endTime = (objectN as IHasEndTime)?.EndTime ?? objectN.StartTime;
|
||||
|
||||
if (objectI.StartTime - endTime > stackThreshold)
|
||||
//We are no longer within stacking range of the previous object.
|
||||
break;
|
||||
|
||||
// HitObjects before the specified update range haven't been reset yet
|
||||
if (n < extendedStartIndex)
|
||||
{
|
||||
objectN.StackHeight = 0;
|
||||
extendedStartIndex = n;
|
||||
}
|
||||
|
||||
/* This is a special case where hticircles are moved DOWN and RIGHT (negative stacking) if they are under the *last* slider in a stacked pattern.
|
||||
* o==o <- slider is at original location
|
||||
* o <- hitCircle has stack of -1
|
||||
* o <- hitCircle has stack of -2
|
||||
*/
|
||||
if (objectN is Slider && Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
|
||||
{
|
||||
int offset = objectI.StackHeight - objectN.StackHeight + 1;
|
||||
for (int j = n + 1; j <= i; j++)
|
||||
{
|
||||
//For each object which was declared under this slider, we will offset it to appear *below* the slider end (rather than above).
|
||||
OsuHitObject objectJ = beatmap.HitObjects[j];
|
||||
if (Vector2.Distance(objectN.EndPosition, objectJ.Position) < stack_distance)
|
||||
objectJ.StackHeight -= offset;
|
||||
}
|
||||
|
||||
//We have hit a slider. We should restart calculation using this as the new base.
|
||||
//Breaking here will mean that the slider still has StackCount of 0, so will be handled in the i-outer-loop.
|
||||
break;
|
||||
}
|
||||
|
||||
if (Vector2.Distance(objectN.Position, objectI.Position) < stack_distance)
|
||||
{
|
||||
//Keep processing as if there are no sliders. If we come across a slider, this gets cancelled out.
|
||||
//NOTE: Sliders with start positions stacking are a special case that is also handled here.
|
||||
|
||||
objectN.StackHeight = objectI.StackHeight + 1;
|
||||
objectI = objectN;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (objectI is Slider)
|
||||
{
|
||||
/* We have hit the first slider in a possible stack.
|
||||
* From this point on, we ALWAYS stack positive regardless.
|
||||
*/
|
||||
while (--n >= 0)
|
||||
{
|
||||
OsuHitObject objectN = beatmap.HitObjects[n];
|
||||
if (objectN is Spinner) continue;
|
||||
|
||||
if (objectI.StartTime - objectN.StartTime > stackThreshold)
|
||||
//We are no longer within stacking range of the previous object.
|
||||
break;
|
||||
|
||||
if (Vector2.Distance(objectN.EndPosition, objectI.Position) < stack_distance)
|
||||
{
|
||||
objectN.StackHeight = objectI.StackHeight + 1;
|
||||
objectI = objectN;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ using osu.Game.Modes.Objects.Drawables;
|
||||
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
|
||||
using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Modes.Osu.UI;
|
||||
|
||||
namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
{
|
||||
@ -29,7 +30,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
|
||||
Position = s.Position;
|
||||
|
||||
//take up full playfield.
|
||||
Size = new Vector2(512);
|
||||
Size = OsuPlayfield.BASE_SIZE;
|
||||
|
||||
spinner = s;
|
||||
|
||||
|
@ -21,6 +21,8 @@ namespace osu.Game.Modes.Osu.Objects
|
||||
private const double hit_window_300 = 30;
|
||||
|
||||
public Vector2 Position { get; set; }
|
||||
public float X => Position.X;
|
||||
public float Y => Position.Y;
|
||||
|
||||
public Vector2 StackedPosition => Position + StackOffset;
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using osu.Game.Modes.Osu.Beatmaps;
|
||||
using osu.Game.Modes.Osu.Objects;
|
||||
@ -180,7 +181,7 @@ namespace osu.Game.Modes.Osu
|
||||
return difficulty;
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
|
||||
// Those values are used as array indices. Be careful when changing them!
|
||||
public enum DifficultyType
|
||||
|
@ -3,6 +3,7 @@
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Objects.Drawables;
|
||||
using osu.Game.Modes.Osu.Beatmaps;
|
||||
using osu.Game.Modes.Osu.Judgements;
|
||||
@ -24,9 +25,9 @@ namespace osu.Game.Modes.Osu.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
|
||||
|
||||
protected override IBeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
|
||||
|
||||
protected override IBeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
|
||||
protected override BeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
|
||||
|
||||
protected override Playfield<OsuHitObject, OsuJudgement> CreatePlayfield() => new OsuPlayfield();
|
||||
|
||||
|
@ -23,6 +23,8 @@ namespace osu.Game.Modes.Osu.UI
|
||||
|
||||
public override bool ProvidingUserCursor => true;
|
||||
|
||||
public static readonly Vector2 BASE_SIZE = new Vector2(512, 384);
|
||||
|
||||
public override Vector2 Size
|
||||
{
|
||||
get
|
||||
@ -34,7 +36,7 @@ namespace osu.Game.Modes.Osu.UI
|
||||
}
|
||||
}
|
||||
|
||||
public OsuPlayfield() : base(512)
|
||||
public OsuPlayfield() : base(BASE_SIZE.X)
|
||||
{
|
||||
Anchor = Anchor.Centre;
|
||||
Origin = Anchor.Centre;
|
||||
@ -94,4 +96,4 @@ namespace osu.Game.Modes.Osu.UI
|
||||
judgementLayer.Add(explosion);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -11,10 +11,11 @@ using System.Linq;
|
||||
using osu.Game.Database;
|
||||
using osu.Game.IO.Serialization;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
|
||||
namespace osu.Game.Modes.Taiko.Beatmaps
|
||||
{
|
||||
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoHitObject>
|
||||
internal class TaikoBeatmapConverter : BeatmapConverter<TaikoHitObject>
|
||||
{
|
||||
/// <summary>
|
||||
/// osu! is generally slower than taiko, so a factor is added to increase
|
||||
@ -38,25 +39,30 @@ namespace osu.Game.Modes.Taiko.Beatmaps
|
||||
/// </summary>
|
||||
private const float taiko_base_distance = 100;
|
||||
|
||||
public Beatmap<TaikoHitObject> Convert(Beatmap original)
|
||||
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
|
||||
|
||||
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original)
|
||||
{
|
||||
// Rewrite the beatmap info to add the slider velocity multiplier
|
||||
BeatmapInfo info = original.BeatmapInfo.DeepClone<BeatmapInfo>();
|
||||
info.Difficulty.SliderMultiplier *= legacy_velocity_multiplier;
|
||||
|
||||
return new Beatmap<TaikoHitObject>(original)
|
||||
Beatmap<TaikoHitObject> converted = base.ConvertBeatmap(original);
|
||||
|
||||
// Post processing step to transform hit objects with the same start time into strong hits
|
||||
converted.HitObjects = converted.HitObjects.GroupBy(t => t.StartTime).Select(x =>
|
||||
{
|
||||
BeatmapInfo = info,
|
||||
HitObjects = original.HitObjects.SelectMany(h => convertHitObject(h, original)).ToList()
|
||||
};
|
||||
TaikoHitObject first = x.First();
|
||||
if (x.Skip(1).Any())
|
||||
first.IsStrong = true;
|
||||
return first;
|
||||
}).ToList();
|
||||
|
||||
return converted;
|
||||
}
|
||||
|
||||
private IEnumerable<TaikoHitObject> convertHitObject(HitObject obj, Beatmap beatmap)
|
||||
protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap)
|
||||
{
|
||||
// Check if this HitObject is already a TaikoHitObject, and return it if so
|
||||
var originalTaiko = obj as TaikoHitObject;
|
||||
if (originalTaiko != null)
|
||||
yield return originalTaiko;
|
||||
|
||||
var distanceData = obj as IHasDistance;
|
||||
var repeatsData = obj as IHasRepeats;
|
||||
var endTimeData = obj as IHasEndTime;
|
||||
|
@ -1,19 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Taiko.Objects;
|
||||
|
||||
namespace osu.Game.Modes.Taiko.Beatmaps
|
||||
{
|
||||
internal class TaikoBeatmapProcessor : IBeatmapProcessor<TaikoHitObject>
|
||||
{
|
||||
public void SetDefaults(TaikoHitObject hitObject, Beatmap<TaikoHitObject> beatmap)
|
||||
{
|
||||
}
|
||||
|
||||
public void PostProcess(Beatmap<TaikoHitObject> beatmap)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Taiko.Beatmaps;
|
||||
using osu.Game.Modes.Taiko.Objects;
|
||||
using System.Collections.Generic;
|
||||
@ -19,6 +20,6 @@ namespace osu.Game.Modes.Taiko
|
||||
return 0;
|
||||
}
|
||||
|
||||
protected override IBeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
|
||||
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
|
||||
}
|
||||
}
|
@ -18,6 +18,7 @@ using osu.Game.Modes.Taiko.Scoring;
|
||||
using osu.Game.Modes.UI;
|
||||
using osu.Game.Modes.Taiko.Replays;
|
||||
using OpenTK;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
|
||||
namespace osu.Game.Modes.Taiko.UI
|
||||
{
|
||||
@ -114,9 +115,7 @@ namespace osu.Game.Modes.Taiko.UI
|
||||
|
||||
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
|
||||
|
||||
protected override IBeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
|
||||
|
||||
protected override IBeatmapProcessor<TaikoHitObject> CreateBeatmapProcessor() => new TaikoBeatmapProcessor();
|
||||
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter();
|
||||
|
||||
protected override Playfield<TaikoHitObject, TaikoJudgement> CreatePlayfield() => new TaikoPlayfield
|
||||
{
|
||||
|
@ -48,7 +48,6 @@
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Beatmaps\TaikoBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\TaikoBeatmapProcessor.cs" />
|
||||
<Compile Include="Judgements\TaikoDrumRollTickJudgement.cs" />
|
||||
<Compile Include="Judgements\TaikoStrongHitJudgement.cs" />
|
||||
<Compile Include="Judgements\TaikoJudgement.cs" />
|
||||
|
@ -7,9 +7,9 @@ using OpenTK;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Tests.Resources;
|
||||
using osu.Game.Modes.Objects.Legacy;
|
||||
using System.Linq;
|
||||
using osu.Game.Audio;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Tests.Beatmaps.Formats
|
||||
{
|
||||
@ -130,16 +130,22 @@ namespace osu.Game.Tests.Beatmaps.Formats
|
||||
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
|
||||
{
|
||||
var beatmap = decoder.Decode(new StreamReader(stream));
|
||||
var slider = beatmap.HitObjects[0] as LegacySlider;
|
||||
Assert.IsNotNull(slider);
|
||||
Assert.AreEqual(new Vector2(192, 168), slider.Position);
|
||||
Assert.AreEqual(956, slider.StartTime);
|
||||
Assert.IsTrue(slider.Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
||||
var hit = beatmap.HitObjects[1] as LegacyHit;
|
||||
Assert.IsNotNull(hit);
|
||||
Assert.AreEqual(new Vector2(304, 56), hit.Position);
|
||||
Assert.AreEqual(1285, hit.StartTime);
|
||||
Assert.IsTrue(hit.Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||
|
||||
var curveData = beatmap.HitObjects[0] as IHasCurve;
|
||||
var positionData = beatmap.HitObjects[0] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.IsNotNull(curveData);
|
||||
Assert.AreEqual(new Vector2(192, 168), positionData.Position);
|
||||
Assert.AreEqual(956, beatmap.HitObjects[0].StartTime);
|
||||
Assert.IsTrue(beatmap.HitObjects[0].Samples.Any(s => s.Name == SampleInfo.HIT_NORMAL));
|
||||
|
||||
positionData = beatmap.HitObjects[1] as IHasPosition;
|
||||
|
||||
Assert.IsNotNull(positionData);
|
||||
Assert.AreEqual(new Vector2(304, 56), positionData.Position);
|
||||
Assert.AreEqual(1285, beatmap.HitObjects[1].StartTime);
|
||||
Assert.IsTrue(beatmap.HitObjects[1].Samples.Any(s => s.Name == SampleInfo.HIT_CLAP));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -56,6 +56,7 @@ namespace osu.Game.Beatmaps
|
||||
public Beatmap(Beatmap original = null)
|
||||
: base(original)
|
||||
{
|
||||
HitObjects = original?.HitObjects;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
using osu.Game.Modes.Objects;
|
||||
using System.Collections.Generic;
|
||||
|
||||
@ -41,6 +42,6 @@ namespace osu.Game.Beatmaps
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract IBeatmapConverter<T> CreateBeatmapConverter();
|
||||
protected abstract BeatmapConverter<T> CreateBeatmapConverter();
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,8 @@ using System.IO;
|
||||
using OpenTK.Graphics;
|
||||
using osu.Game.Beatmaps.Events;
|
||||
using osu.Game.Beatmaps.Timing;
|
||||
using osu.Game.Modes.Objects;
|
||||
using osu.Game.Beatmaps.Legacy;
|
||||
using osu.Game.Modes.Objects.Legacy;
|
||||
|
||||
namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
@ -29,6 +29,8 @@ namespace osu.Game.Beatmaps.Formats
|
||||
// TODO: Not sure how far back to go, or differences between versions
|
||||
}
|
||||
|
||||
private HitObjectParser parser;
|
||||
|
||||
private LegacySampleBank defaultSampleBank;
|
||||
private int defaultSampleVolume = 100;
|
||||
|
||||
@ -84,6 +86,22 @@ namespace osu.Game.Beatmaps.Formats
|
||||
break;
|
||||
case @"Mode":
|
||||
beatmap.BeatmapInfo.RulesetID = int.Parse(val);
|
||||
|
||||
switch (beatmap.BeatmapInfo.RulesetID)
|
||||
{
|
||||
case 0:
|
||||
parser = new Modes.Objects.Legacy.Osu.HitObjectParser();
|
||||
break;
|
||||
case 1:
|
||||
parser = new Modes.Objects.Legacy.Taiko.HitObjectParser();
|
||||
break;
|
||||
case 2:
|
||||
parser = new Modes.Objects.Legacy.Catch.HitObjectParser();
|
||||
break;
|
||||
case 3:
|
||||
parser = new Modes.Objects.Legacy.Mania.HitObjectParser();
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case @"LetterboxInBreaks":
|
||||
beatmap.BeatmapInfo.LetterboxInBreaks = int.Parse(val) == 1;
|
||||
@ -303,8 +321,6 @@ namespace osu.Game.Beatmaps.Formats
|
||||
{
|
||||
beatmap.BeatmapInfo.BeatmapVersion = beatmapVersion;
|
||||
|
||||
HitObjectParser parser = new LegacyHitObjectParser();
|
||||
|
||||
Section section = Section.None;
|
||||
bool hasCustomColours = false;
|
||||
|
||||
|
@ -1,21 +0,0 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Beatmap for another mode.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of HitObject stored in the Beatmap.</typeparam>
|
||||
public interface IBeatmapConverter<T> where T : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Beatmap to another mode.
|
||||
/// </summary>
|
||||
/// <param name="original">The original Beatmap.</param>
|
||||
/// <returns>The converted Beatmap.</returns>
|
||||
Beatmap<T> Convert(Beatmap original);
|
||||
}
|
||||
}
|
90
osu.Game/Modes/Beatmaps/BeatmapConverter.cs
Normal file
90
osu.Game/Modes/Beatmaps/BeatmapConverter.cs
Normal file
@ -0,0 +1,90 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using osu.Game.Modes.Objects;
|
||||
using osu.Game.Beatmaps;
|
||||
|
||||
namespace osu.Game.Modes.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Converts a Beatmap for another mode.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of HitObject stored in the Beatmap.</typeparam>
|
||||
public abstract class BeatmapConverter<T> where T : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Checks if a Beatmap can be converted using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The Beatmap to check.</param>
|
||||
/// <returns>Whether the Beatmap can be converted using this Beatmap Converter.</returns>
|
||||
public bool CanConvert(Beatmap beatmap) => ValidConversionTypes.All(t => beatmap.HitObjects.Any(t.IsInstanceOfType));
|
||||
|
||||
/// <summary>
|
||||
/// Converts a Beatmap using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="original">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted Beatmap.</returns>
|
||||
public Beatmap<T> Convert(Beatmap original)
|
||||
{
|
||||
// We always operate on a clone of the original beatmap, to not modify it game-wide
|
||||
return ConvertBeatmap(new Beatmap(original));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs the conversion of a Beatmap using this Beatmap Converter.
|
||||
/// </summary>
|
||||
/// <param name="original">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted Beatmap.</returns>
|
||||
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original)
|
||||
{
|
||||
return new Beatmap<T>
|
||||
{
|
||||
BeatmapInfo = original.BeatmapInfo,
|
||||
TimingInfo = original.TimingInfo,
|
||||
HitObjects = original.HitObjects.SelectMany(h => convert(h, original)).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Converts a hit object.
|
||||
/// </summary>
|
||||
/// <param name="original">The hit object to convert.</param>
|
||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted hit object.</returns>
|
||||
private IEnumerable<T> convert(HitObject original, Beatmap beatmap)
|
||||
{
|
||||
// Check if the hitobject is already the converted type
|
||||
T tObject = original as T;
|
||||
if (tObject != null)
|
||||
{
|
||||
yield return tObject;
|
||||
yield break;
|
||||
}
|
||||
|
||||
// Convert the hit object
|
||||
foreach (var obj in ConvertHitObject(original, beatmap))
|
||||
{
|
||||
if (obj == null)
|
||||
continue;
|
||||
|
||||
yield return obj;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The types of HitObjects that can be converted to be used for this Beatmap.
|
||||
/// </summary>
|
||||
protected abstract IEnumerable<Type> ValidConversionTypes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Performs the conversion of a hit object.
|
||||
/// </summary>
|
||||
/// <param name="original">The hit object to convert.</param>
|
||||
/// <param name="beatmap">The un-converted Beatmap.</param>
|
||||
/// <returns>The converted hit object.</returns>
|
||||
protected abstract IEnumerable<T> ConvertHitObject(HitObject original, Beatmap beatmap);
|
||||
}
|
||||
}
|
@ -1,15 +1,16 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Modes.Objects;
|
||||
|
||||
namespace osu.Game.Beatmaps
|
||||
namespace osu.Game.Modes.Beatmaps
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes a post-converted Beatmap.
|
||||
/// </summary>
|
||||
/// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam>
|
||||
public interface IBeatmapProcessor<TObject>
|
||||
public class BeatmapProcessor<TObject>
|
||||
where TObject : HitObject
|
||||
{
|
||||
/// <summary>
|
||||
@ -19,6 +20,6 @@ namespace osu.Game.Beatmaps
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="beatmap">The Beatmap to process.</param>
|
||||
void PostProcess(Beatmap<TObject> beatmap);
|
||||
public virtual void PostProcess(Beatmap<TObject> beatmap) { }
|
||||
}
|
||||
}
|
17
osu.Game/Modes/Objects/Legacy/Catch/Hit.cs
Normal file
17
osu.Game/Modes/Objects/Legacy/Catch/Hit.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Catch
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!catch Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hit : HitObject, IHasCombo, IHasXPosition
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
45
osu.Game/Modes/Objects/Legacy/Catch/HitObjectParser.cs
Normal file
45
osu.Game/Modes/Objects/Legacy/Catch/HitObjectParser.cs
Normal file
@ -0,0 +1,45 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Catch
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy osu!catch Beatmaps.
|
||||
/// </summary>
|
||||
internal class HitObjectParser : Legacy.HitObjectParser
|
||||
{
|
||||
protected override HitObject CreateHit(Vector2 position, bool newCombo)
|
||||
{
|
||||
return new Hit
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
|
||||
{
|
||||
return new Slider
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSpinner(Vector2 position, double endTime)
|
||||
{
|
||||
return new Spinner
|
||||
{
|
||||
EndTime = endTime
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
17
osu.Game/Modes/Objects/Legacy/Catch/Slider.cs
Normal file
17
osu.Game/Modes/Objects/Legacy/Catch/Slider.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Catch
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!catch Slider-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Slider : CurvedHitObject, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
@ -3,12 +3,12 @@
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy
|
||||
namespace osu.Game.Modes.Objects.Legacy.Catch
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy Spinner-type, used for parsing Beatmaps.
|
||||
/// Legacy osu!catch Spinner-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal class LegacySpinner : HitObject, IHasEndTime
|
||||
internal sealed class Spinner : HitObject, IHasEndTime
|
||||
{
|
||||
public double EndTime { get; set; }
|
||||
|
@ -6,20 +6,22 @@ using osu.Game.Modes.Objects.Types;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using osu.Game.Modes.Objects.Legacy;
|
||||
using osu.Game.Beatmaps.Formats;
|
||||
using osu.Game.Audio;
|
||||
|
||||
namespace osu.Game.Modes.Objects
|
||||
namespace osu.Game.Modes.Objects.Legacy
|
||||
{
|
||||
internal class LegacyHitObjectParser : HitObjectParser
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy Beatmaps.
|
||||
/// </summary>
|
||||
internal abstract class HitObjectParser : Objects.HitObjectParser
|
||||
{
|
||||
public override HitObject Parse(string text)
|
||||
{
|
||||
string[] split = text.Split(',');
|
||||
var type = (LegacyHitObjectType)int.Parse(split[3]) & ~LegacyHitObjectType.ColourHax;
|
||||
bool combo = type.HasFlag(LegacyHitObjectType.NewCombo);
|
||||
type &= ~LegacyHitObjectType.NewCombo;
|
||||
var type = (HitObjectType)int.Parse(split[3]) & ~HitObjectType.ColourHax;
|
||||
bool combo = type.HasFlag(HitObjectType.NewCombo);
|
||||
type &= ~HitObjectType.NewCombo;
|
||||
|
||||
int sampleVolume = 0;
|
||||
string normalSampleBank = null;
|
||||
@ -27,22 +29,18 @@ namespace osu.Game.Modes.Objects
|
||||
|
||||
HitObject result;
|
||||
|
||||
if ((type & LegacyHitObjectType.Circle) > 0)
|
||||
if ((type & HitObjectType.Circle) > 0)
|
||||
{
|
||||
result = new LegacyHit
|
||||
{
|
||||
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
|
||||
NewCombo = combo
|
||||
};
|
||||
result = CreateHit(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo);
|
||||
|
||||
if (split.Length > 5)
|
||||
readCustomSampleBanks(split[5], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
|
||||
}
|
||||
else if ((type & LegacyHitObjectType.Slider) > 0)
|
||||
else if ((type & HitObjectType.Slider) > 0)
|
||||
{
|
||||
CurveType curveType = CurveType.Catmull;
|
||||
double length = 0;
|
||||
List<Vector2> points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
|
||||
var points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
|
||||
|
||||
string[] pointsplit = split[5].Split('|');
|
||||
foreach (string t in pointsplit)
|
||||
@ -68,11 +66,7 @@ namespace osu.Game.Modes.Objects
|
||||
}
|
||||
|
||||
string[] temp = t.Split(':');
|
||||
Vector2 v = new Vector2(
|
||||
(int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture),
|
||||
(int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)
|
||||
);
|
||||
points.Add(v);
|
||||
points.Add(new Vector2((int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture), (int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)));
|
||||
}
|
||||
|
||||
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
|
||||
@ -83,37 +77,26 @@ namespace osu.Game.Modes.Objects
|
||||
if (split.Length > 7)
|
||||
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
|
||||
|
||||
result = new LegacySlider
|
||||
{
|
||||
ControlPoints = points,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatCount = repeatCount,
|
||||
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
|
||||
NewCombo = combo
|
||||
};
|
||||
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount);
|
||||
|
||||
if (split.Length > 10)
|
||||
readCustomSampleBanks(split[10], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
|
||||
}
|
||||
else if ((type & LegacyHitObjectType.Spinner) > 0)
|
||||
else if ((type & HitObjectType.Spinner) > 0)
|
||||
{
|
||||
result = new LegacySpinner
|
||||
{
|
||||
EndTime = Convert.ToDouble(split[5], CultureInfo.InvariantCulture)
|
||||
};
|
||||
result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture));
|
||||
|
||||
if (split.Length > 6)
|
||||
readCustomSampleBanks(split[6], ref normalSampleBank, ref addSampleBank, ref sampleVolume);
|
||||
}
|
||||
else if ((type & LegacyHitObjectType.Hold) > 0)
|
||||
else if ((type & HitObjectType.Hold) > 0)
|
||||
{
|
||||
// Note: Hold is generated by BMS converts
|
||||
|
||||
// Todo: Apparently end time is determined by samples??
|
||||
// Shouldn't need implementation until mania
|
||||
|
||||
result = new LegacyHold
|
||||
result = new Hold
|
||||
{
|
||||
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
|
||||
NewCombo = combo
|
||||
@ -191,6 +174,34 @@ namespace osu.Game.Modes.Objects
|
||||
sampleVolume = split.Length > 3 ? int.Parse(split[3]) : 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a legacy Hit-type hit object.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the hit object.</param>
|
||||
/// <param name="newCombo">Whether the hit object creates a new combo.</param>
|
||||
/// <returns>The hit object.</returns>
|
||||
protected abstract HitObject CreateHit(Vector2 position, bool newCombo);
|
||||
|
||||
/// <summary>
|
||||
/// Creats a legacy Slider-type hit object.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the hit object.</param>
|
||||
/// <param name="newCombo">Whether the hit object creates a new combo.</param>
|
||||
/// <param name="controlPoints">The slider control points.</param>
|
||||
/// <param name="length">The slider length.</param>
|
||||
/// <param name="curveType">The slider curve type.</param>
|
||||
/// <param name="repeatCount">The slider repeat count.</param>
|
||||
/// <returns>The hit object.</returns>
|
||||
protected abstract HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a legacy Spinner-type hit object.
|
||||
/// </summary>
|
||||
/// <param name="position">The position of the hit object.</param>
|
||||
/// <param name="endTime">The spinner end time.</param>
|
||||
/// <returns>The hit object.</returns>
|
||||
protected abstract HitObject CreateSpinner(Vector2 position, double endTime);
|
||||
|
||||
[Flags]
|
||||
private enum LegacySoundType
|
||||
{
|
@ -6,7 +6,7 @@ using System;
|
||||
namespace osu.Game.Modes.Objects.Legacy
|
||||
{
|
||||
[Flags]
|
||||
public enum LegacyHitObjectType
|
||||
public enum HitObjectType
|
||||
{
|
||||
Circle = 1 << 0,
|
||||
Slider = 1 << 1,
|
@ -9,10 +9,14 @@ namespace osu.Game.Modes.Objects.Legacy
|
||||
/// <summary>
|
||||
/// Legacy Hold-type, used for parsing "specials" in beatmaps.
|
||||
/// </summary>
|
||||
public sealed class LegacyHold : HitObject, IHasPosition, IHasCombo, IHasHold
|
||||
internal sealed class Hold : HitObject, IHasPosition, IHasCombo, IHasHold
|
||||
{
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
public float X => Position.X;
|
||||
|
||||
public float Y => Position.Y;
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
17
osu.Game/Modes/Objects/Legacy/Mania/Hit.cs
Normal file
17
osu.Game/Modes/Objects/Legacy/Mania/Hit.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Mania
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!mania Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hit : HitObject, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
46
osu.Game/Modes/Objects/Legacy/Mania/HitObjectParser.cs
Normal file
46
osu.Game/Modes/Objects/Legacy/Mania/HitObjectParser.cs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Mania
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy osu!mania Beatmaps.
|
||||
/// </summary>
|
||||
internal class HitObjectParser : Legacy.HitObjectParser
|
||||
{
|
||||
protected override HitObject CreateHit(Vector2 position, bool newCombo)
|
||||
{
|
||||
return new Hit
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
|
||||
{
|
||||
return new Slider
|
||||
{
|
||||
X = position.X,
|
||||
NewCombo = newCombo,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSpinner(Vector2 position, double endTime)
|
||||
{
|
||||
return new Spinner
|
||||
{
|
||||
X = position.X,
|
||||
EndTime = endTime
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
17
osu.Game/Modes/Objects/Legacy/Mania/Slider.cs
Normal file
17
osu.Game/Modes/Objects/Legacy/Mania/Slider.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Mania
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!mania Slider-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Slider : CurvedHitObject, IHasXPosition, IHasCombo
|
||||
{
|
||||
public float X { get; set; }
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
19
osu.Game/Modes/Objects/Legacy/Mania/Spinner.cs
Normal file
19
osu.Game/Modes/Objects/Legacy/Mania/Spinner.cs
Normal file
@ -0,0 +1,19 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Mania
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!mania Spinner-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Spinner : HitObject, IHasEndTime, IHasXPosition
|
||||
{
|
||||
public double EndTime { get; set; }
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public float X { get; set; }
|
||||
}
|
||||
}
|
@ -4,15 +4,19 @@
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy
|
||||
namespace osu.Game.Modes.Objects.Legacy.Osu
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy Hit-type, used for parsing Beatmaps.
|
||||
/// Legacy osu! Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
public sealed class LegacyHit : HitObject, IHasPosition, IHasCombo
|
||||
internal sealed class Hit : HitObject, IHasPosition, IHasCombo
|
||||
{
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
public float X => Position.X;
|
||||
|
||||
public float Y => Position.Y;
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
46
osu.Game/Modes/Objects/Legacy/Osu/HitObjectParser.cs
Normal file
46
osu.Game/Modes/Objects/Legacy/Osu/HitObjectParser.cs
Normal file
@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Osu
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy osu! Beatmaps.
|
||||
/// </summary>
|
||||
internal class HitObjectParser : Legacy.HitObjectParser
|
||||
{
|
||||
protected override HitObject CreateHit(Vector2 position, bool newCombo)
|
||||
{
|
||||
return new Hit
|
||||
{
|
||||
Position = position,
|
||||
NewCombo = newCombo,
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
|
||||
{
|
||||
return new Slider
|
||||
{
|
||||
Position = position,
|
||||
NewCombo = newCombo,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSpinner(Vector2 position, double endTime)
|
||||
{
|
||||
return new Spinner
|
||||
{
|
||||
Position = position,
|
||||
EndTime = endTime
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
@ -4,15 +4,19 @@
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy
|
||||
namespace osu.Game.Modes.Objects.Legacy.Osu
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy Slider-type, used for parsing Beatmaps.
|
||||
/// Legacy osu! Slider-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
public sealed class LegacySlider : CurvedHitObject, IHasPosition, IHasCombo
|
||||
internal sealed class Slider : CurvedHitObject, IHasPosition, IHasCombo
|
||||
{
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
public float X => Position.X;
|
||||
|
||||
public float Y => Position.Y;
|
||||
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
24
osu.Game/Modes/Objects/Legacy/Osu/Spinner.cs
Normal file
24
osu.Game/Modes/Objects/Legacy/Osu/Spinner.cs
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using OpenTK;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Osu
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu! Spinner-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Spinner : HitObject, IHasEndTime, IHasPosition
|
||||
{
|
||||
public double EndTime { get; set; }
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
|
||||
public Vector2 Position { get; set; }
|
||||
|
||||
public float X => Position.X;
|
||||
|
||||
public float Y => Position.Y;
|
||||
}
|
||||
}
|
15
osu.Game/Modes/Objects/Legacy/Taiko/Hit.cs
Normal file
15
osu.Game/Modes/Objects/Legacy/Taiko/Hit.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Taiko
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!taiko Hit-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Hit : HitObject, IHasCombo
|
||||
{
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
43
osu.Game/Modes/Objects/Legacy/Taiko/HitObjectParser.cs
Normal file
43
osu.Game/Modes/Objects/Legacy/Taiko/HitObjectParser.cs
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using OpenTK;
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Taiko
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObjectParser to parse legacy osu!taiko Beatmaps.
|
||||
/// </summary>
|
||||
internal class HitObjectParser : Legacy.HitObjectParser
|
||||
{
|
||||
protected override HitObject CreateHit(Vector2 position, bool newCombo)
|
||||
{
|
||||
return new Hit
|
||||
{
|
||||
NewCombo = newCombo,
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSlider(Vector2 position, bool newCombo, List<Vector2> controlPoints, double length, CurveType curveType, int repeatCount)
|
||||
{
|
||||
return new Slider
|
||||
{
|
||||
NewCombo = newCombo,
|
||||
ControlPoints = controlPoints,
|
||||
Distance = length,
|
||||
CurveType = curveType,
|
||||
RepeatCount = repeatCount
|
||||
};
|
||||
}
|
||||
|
||||
protected override HitObject CreateSpinner(Vector2 position, double endTime)
|
||||
{
|
||||
return new Spinner
|
||||
{
|
||||
EndTime = endTime
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
15
osu.Game/Modes/Objects/Legacy/Taiko/Slider.cs
Normal file
15
osu.Game/Modes/Objects/Legacy/Taiko/Slider.cs
Normal file
@ -0,0 +1,15 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Taiko
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!taiko Slider-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Slider : CurvedHitObject, IHasCombo
|
||||
{
|
||||
public bool NewCombo { get; set; }
|
||||
}
|
||||
}
|
17
osu.Game/Modes/Objects/Legacy/Taiko/Spinner.cs
Normal file
17
osu.Game/Modes/Objects/Legacy/Taiko/Spinner.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
using osu.Game.Modes.Objects.Types;
|
||||
|
||||
namespace osu.Game.Modes.Objects.Legacy.Taiko
|
||||
{
|
||||
/// <summary>
|
||||
/// Legacy osu!taiko Spinner-type, used for parsing Beatmaps.
|
||||
/// </summary>
|
||||
internal sealed class Spinner : HitObject, IHasEndTime
|
||||
{
|
||||
public double EndTime { get; set; }
|
||||
|
||||
public double Duration => EndTime - StartTime;
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@ namespace osu.Game.Modes.Objects.Types
|
||||
/// <summary>
|
||||
/// A HitObject that has a starting position.
|
||||
/// </summary>
|
||||
public interface IHasPosition
|
||||
public interface IHasPosition : IHasXPosition, IHasYPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting position of the HitObject.
|
||||
|
17
osu.Game/Modes/Objects/Types/IHasXPosition.cs
Normal file
17
osu.Game/Modes/Objects/Types/IHasXPosition.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
|
||||
namespace osu.Game.Modes.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObject that has a starting X-position.
|
||||
/// </summary>
|
||||
public interface IHasXPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting X-position of this HitObject.
|
||||
/// </summary>
|
||||
float X { get; }
|
||||
}
|
||||
}
|
17
osu.Game/Modes/Objects/Types/IHasYPosition.cs
Normal file
17
osu.Game/Modes/Objects/Types/IHasYPosition.cs
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
|
||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||
|
||||
|
||||
namespace osu.Game.Modes.Objects.Types
|
||||
{
|
||||
/// <summary>
|
||||
/// A HitObject that has a starting Y-position.
|
||||
/// </summary>
|
||||
public interface IHasYPosition
|
||||
{
|
||||
/// <summary>
|
||||
/// The starting Y-position of this HitObject.
|
||||
/// </summary>
|
||||
float Y { get; }
|
||||
}
|
||||
}
|
@ -17,6 +17,7 @@ using System.Linq;
|
||||
using osu.Game.Modes.Replays;
|
||||
using osu.Game.Modes.Scoring;
|
||||
using OpenTK;
|
||||
using osu.Game.Modes.Beatmaps;
|
||||
|
||||
namespace osu.Game.Modes.UI
|
||||
{
|
||||
@ -119,8 +120,12 @@ namespace osu.Game.Modes.UI
|
||||
|
||||
RelativeSizeAxes = Axes.Both;
|
||||
|
||||
IBeatmapConverter<TObject> converter = CreateBeatmapConverter();
|
||||
IBeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
|
||||
BeatmapConverter<TObject> converter = CreateBeatmapConverter();
|
||||
BeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
|
||||
|
||||
// Check if the beatmap can be converted
|
||||
if (!converter.CanConvert(beatmap.Beatmap))
|
||||
throw new BeatmapInvalidForModeException($"{nameof(Beatmap)} can't be converted for the current ruleset.");
|
||||
|
||||
// Convert the beatmap
|
||||
Beatmap = converter.Convert(beatmap.Beatmap);
|
||||
@ -136,7 +141,6 @@ namespace osu.Game.Modes.UI
|
||||
applyMods(beatmap.Mods.Value);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Applies the active mods to this HitRenderer.
|
||||
/// </summary>
|
||||
@ -150,18 +154,18 @@ namespace osu.Game.Modes.UI
|
||||
mod.Apply(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a converter to convert Beatmap to a specific mode.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap converter.</returns>
|
||||
protected abstract IBeatmapConverter<TObject> CreateBeatmapConverter();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a processor to perform post-processing operations
|
||||
/// on HitObjects in converted Beatmaps.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap processor.</returns>
|
||||
protected abstract IBeatmapProcessor<TObject> CreateBeatmapProcessor();
|
||||
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
|
||||
|
||||
/// <summary>
|
||||
/// Creates a converter to convert Beatmap to a specific mode.
|
||||
/// </summary>
|
||||
/// <returns>The Beatmap converter.</returns>
|
||||
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -268,4 +272,12 @@ namespace osu.Game.Modes.UI
|
||||
/// <returns>The Playfield.</returns>
|
||||
protected abstract Playfield<TObject, TJudgement> CreatePlayfield();
|
||||
}
|
||||
|
||||
public class BeatmapInvalidForModeException : Exception
|
||||
{
|
||||
public BeatmapInvalidForModeException(string text)
|
||||
: base(text)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -60,8 +60,8 @@ namespace osu.Game.Screens.Play
|
||||
private PauseOverlay pauseOverlay;
|
||||
private FailOverlay failOverlay;
|
||||
|
||||
[BackgroundDependencyLoader]
|
||||
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config)
|
||||
[BackgroundDependencyLoader(permitNulls: true)]
|
||||
private void load(AudioManager audio, BeatmapDatabase beatmaps, OsuConfigManager config, OsuGame osu)
|
||||
{
|
||||
dimLevel = config.GetBindable<int>(OsuConfig.DimLevel);
|
||||
mouseWheelDisabled = config.GetBindable<bool>(OsuConfig.MouseDisableWheel);
|
||||
@ -76,6 +76,19 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
if (Beatmap == null)
|
||||
throw new Exception("Beatmap was not loaded");
|
||||
|
||||
try
|
||||
{
|
||||
// Try using the preferred user ruleset
|
||||
ruleset = osu == null ? Beatmap.BeatmapInfo.Ruleset.CreateInstance() : osu.Ruleset.Value.CreateInstance();
|
||||
HitRenderer = ruleset.CreateHitRendererWith(Beatmap);
|
||||
}
|
||||
catch (BeatmapInvalidForModeException)
|
||||
{
|
||||
// Default to the beatmap ruleset
|
||||
ruleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||
HitRenderer = ruleset.CreateHitRendererWith(Beatmap);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@ -102,12 +115,6 @@ namespace osu.Game.Screens.Play
|
||||
sourceClock.Reset();
|
||||
});
|
||||
|
||||
ruleset = Beatmap.BeatmapInfo.Ruleset.CreateInstance();
|
||||
|
||||
// Todo: This should be done as early as possible, and should check if the hit renderer
|
||||
// can actually convert the hit objects... Somehow...
|
||||
HitRenderer = ruleset.CreateHitRendererWith(Beatmap);
|
||||
|
||||
scoreProcessor = HitRenderer.CreateScoreProcessor();
|
||||
|
||||
hudOverlay = new StandardHudOverlay()
|
||||
|
@ -72,8 +72,8 @@
|
||||
<Compile Include="Audio\SampleInfo.cs" />
|
||||
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
|
||||
<Compile Include="Beatmaps\DifficultyCalculator.cs" />
|
||||
<Compile Include="Beatmaps\IBeatmapConverter.cs" />
|
||||
<Compile Include="Beatmaps\IBeatmapProcessor.cs" />
|
||||
<Compile Include="Modes\Beatmaps\BeatmapConverter.cs" />
|
||||
<Compile Include="Modes\Beatmaps\BeatmapProcessor.cs" />
|
||||
<Compile Include="Beatmaps\Legacy\LegacyBeatmap.cs" />
|
||||
<Compile Include="Beatmaps\Timing\TimeSignatures.cs" />
|
||||
<Compile Include="Beatmaps\Timing\TimingInfo.cs" />
|
||||
@ -104,6 +104,21 @@
|
||||
<Compile Include="IO\Serialization\IJsonSerializable.cs" />
|
||||
<Compile Include="IPC\ScoreIPCChannel.cs" />
|
||||
<Compile Include="Modes\BeatmapStatistic.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Catch\Hit.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Catch\HitObjectParser.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Catch\Slider.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Catch\Spinner.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Mania\Hit.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Mania\HitObjectParser.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Mania\Slider.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Mania\Spinner.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Osu\HitObjectParser.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Taiko\Hit.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Taiko\HitObjectParser.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Taiko\Slider.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Taiko\Spinner.cs" />
|
||||
<Compile Include="Modes\Objects\Types\IHasXPosition.cs" />
|
||||
<Compile Include="Modes\Objects\Types\IHasYPosition.cs" />
|
||||
<Compile Include="Modes\Replays\Replay.cs" />
|
||||
<Compile Include="Modes\Judgements\DrawableJudgement.cs" />
|
||||
<Compile Include="Modes\Judgements\IPartialJudgement.cs" />
|
||||
@ -115,11 +130,11 @@
|
||||
<Compile Include="Modes\Objects\BezierApproximator.cs" />
|
||||
<Compile Include="Modes\Objects\CircularArcApproximator.cs" />
|
||||
<Compile Include="Modes\Objects\CurvedHitObject.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\LegacyHit.cs" />
|
||||
<Compile Include="Modes\Objects\LegacyHitObjectParser.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\LegacyHold.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\LegacySlider.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\LegacySpinner.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Osu\Hit.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\HitObjectParser.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Hold.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Osu\Slider.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\Osu\Spinner.cs" />
|
||||
<Compile Include="Modes\Objects\SliderCurve.cs" />
|
||||
<Compile Include="Modes\Objects\Types\CurveType.cs" />
|
||||
<Compile Include="Modes\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
|
||||
@ -132,7 +147,7 @@
|
||||
<Compile Include="Modes\Objects\Types\IHasRepeats.cs" />
|
||||
<Compile Include="Modes\Objects\Types\IHasPosition.cs" />
|
||||
<Compile Include="Modes\Objects\Types\IHasHold.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\LegacyHitObjectType.cs" />
|
||||
<Compile Include="Modes\Objects\Legacy\HitObjectType.cs" />
|
||||
<Compile Include="Modes\Replays\ReplayButtonState.cs" />
|
||||
<Compile Include="Modes\Replays\ReplayFrame.cs" />
|
||||
<Compile Include="Database\RulesetDatabase.cs" />
|
||||
|
Reference in New Issue
Block a user