1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 01:27:29 +08:00

Rework/rewrite beatmap parsing to parse to base hit objects, which mode-specific beatmap converters can then use.

This commit is contained in:
smoogipooo 2017-03-13 19:15:25 +09:00
parent c76a495d3d
commit f50e0bbf3c
58 changed files with 470 additions and 322 deletions

View File

@ -75,7 +75,7 @@ namespace osu.Desktop.VisualTests.Tests
add(new DrawableSpinner(new Spinner
{
StartTime = framedClock.CurrentTime + 600,
Length = 1000,
EndTime = framedClock.CurrentTime + 1600,
Position = new Vector2(0, 0),
}));
break;

View File

@ -10,7 +10,7 @@ namespace osu.Game.Modes.Catch.Beatmaps
{
internal class CatchBeatmapConverter : IBeatmapConverter<CatchBaseHit>
{
public Beatmap<CatchBaseHit> Convert(Beatmap original)
public Beatmap<CatchBaseHit> ConvertBeatmap(Beatmap original)
{
return new Beatmap<CatchBaseHit>(original)
{

View File

@ -5,7 +5,6 @@ using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Modes.Catch.UI;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
@ -88,8 +87,6 @@ namespace osu.Game.Modes.Catch
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
}
}

View File

@ -1,12 +1,14 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
using OpenTK;
// 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;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Catch.Objects
{
public abstract class CatchBaseHit : HitObject
public abstract class CatchBaseHit : HitObject, IHasPosition
{
public float Position;
public Vector2 Position { get; set; }
}
}

View File

@ -1,40 +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 System.Collections.Generic;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Beatmaps;
namespace osu.Game.Modes.Catch.Objects
{
internal class CatchConverter : HitObjectConverter<CatchBaseHit>
{
public override List<CatchBaseHit> Convert(Beatmap beatmap)
{
List<CatchBaseHit> output = new List<CatchBaseHit>();
foreach (HitObject i in beatmap.HitObjects)
{
CatchBaseHit h = i as CatchBaseHit;
if (h == null)
{
OsuHitObject o = i as OsuHitObject;
if (o == null) throw new HitObjectConvertException(@"Catch", i);
h = new Fruit
{
StartTime = o.StartTime,
Position = o.Position.X,
};
}
output.Add(h);
}
return output;
}
}
}

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Catch.Objects.Drawable
{
@ -21,7 +22,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
Origin = Anchor.Centre;
Scale = new Vector2(0.1f);
RelativePositionAxes = Axes.Y;
Position = new Vector2(h.Position, -0.1f);
Position = new Vector2(h.Position.X, -0.1f);
}
[BackgroundDependencyLoader]
@ -29,8 +30,11 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{
Texture = textures.Get(@"Menu/logo");
Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position, -0.1f), EndValue = new Vector2(h.Position, 0.9f) });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 });
double endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
double duration = endTime - h.StartTime;
Transforms.Add(new TransformPosition { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = new Vector2(h.Position.X, -0.1f), EndValue = new Vector2(h.Position.X, 0.9f) });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true);
}
}

View File

@ -50,7 +50,6 @@
<Compile Include="Beatmaps\CatchBeatmapConverter.cs" />
<Compile Include="CatchDifficultyCalculator.cs" />
<Compile Include="Objects\CatchBaseHit.cs" />
<Compile Include="Objects\CatchConverter.cs" />
<Compile Include="Objects\Drawable\DrawableFruit.cs" />
<Compile Include="Objects\Droplet.cs" />
<Compile Include="Objects\Fruit.cs" />

View File

@ -10,7 +10,7 @@ namespace osu.Game.Modes.Mania.Beatmaps
{
internal class ManiaBeatmapConverter : IBeatmapConverter<ManiaBaseHit>
{
public Beatmap<ManiaBaseHit> Convert(Beatmap original)
public Beatmap<ManiaBaseHit> ConvertBeatmap(Beatmap original)
{
return new Beatmap<ManiaBaseHit>(original)
{

View File

@ -4,7 +4,6 @@
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Modes.Mania.UI;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System.Collections.Generic;
@ -103,8 +102,6 @@ namespace osu.Game.Modes.Mania
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
}
}

View File

@ -2,11 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Mania.Objects
{
public abstract class ManiaBaseHit : HitObject
public abstract class ManiaBaseHit : HitObject, IHasEndTime
{
public int Column;
public double EndTime { get; set; }
public double Duration { get; set; }
}
}

View File

@ -1,48 +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 System;
using System.Collections.Generic;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Beatmaps;
namespace osu.Game.Modes.Mania.Objects
{
internal class ManiaConverter : HitObjectConverter<ManiaBaseHit>
{
private readonly int columns;
public ManiaConverter(int columns)
{
this.columns = columns;
}
public override List<ManiaBaseHit> Convert(Beatmap beatmap)
{
List<ManiaBaseHit> output = new List<ManiaBaseHit>();
foreach (HitObject i in beatmap.HitObjects)
{
ManiaBaseHit h = i as ManiaBaseHit;
if (h == null)
{
OsuHitObject o = i as OsuHitObject;
if (o == null) throw new HitObjectConvertException(@"Mania", i);
h = new Note
{
StartTime = o.StartTime,
Column = (int)Math.Round(o.Position.X / 512 * columns)
};
}
output.Add(h);
}
return output;
}
}
}

View File

@ -52,7 +52,6 @@
<Compile Include="Objects\Drawable\DrawableNote.cs" />
<Compile Include="Objects\HoldNote.cs" />
<Compile Include="Objects\ManiaBaseHit.cs" />
<Compile Include="Objects\ManiaConverter.cs" />
<Compile Include="Objects\Note.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\ManiaHitRenderer.cs" />

View File

@ -8,29 +8,90 @@ 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 OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Beatmaps
{
internal class OsuBeatmapConverter : IBeatmapConverter<OsuHitObject>
{
public Beatmap<OsuHitObject> Convert(Beatmap original)
public Beatmap<OsuHitObject> ConvertBeatmap(Beatmap original)
{
return new Beatmap<OsuHitObject>(original)
{
HitObjects = convertHitObject(original.HitObjects, original.BeatmapInfo?.StackLeniency ?? 0.7f)
HitObjects = convertHitObjects(original.HitObjects, original.BeatmapInfo?.StackLeniency ?? 0.7f)
};
}
private List<OsuHitObject> convertHitObject(List<HitObject> hitObjects, float stackLeniency)
private OsuHitObject convertHitObject(HitObject original)
{
IHasCurve ihc = original as IHasCurve;
IHasDistance ihd = original as IHasDistance;
IHasEndTime ihet = original as IHasEndTime;
IHasPosition ihp = original as IHasPosition;
IHasRepeats ihr = original as IHasRepeats;
IHasCombo ihco = original as IHasCombo;
if (ihc != null)
{
return new Slider
{
StartTime = original.StartTime,
Sample = original.Sample,
CurveType = ihc.CurveType,
ControlPoints = ihc.ControlPoints,
Position = ihp?.Position ?? Vector2.Zero,
ComboColour = ihco?.ComboColour ?? Color4.White,
ComboIndex = ihco?.ComboIndex ?? 0,
NewCombo = ihco?.NewCombo ?? false,
Length = ihd?.Distance ?? 0,
RepeatCount = ihr?.RepeatCount ?? 0
};
}
if (ihet != null)
{
return new Spinner
{
StartTime = original.StartTime,
Sample = original.Sample,
Position = new Vector2(512, 384) / 2,
EndTime = ihet.EndTime,
ComboColour = ihco?.ComboColour ?? Color4.White,
ComboIndex = ihco?.ComboIndex ?? 0,
NewCombo = ihco?.NewCombo ?? false,
};
}
return new HitCircle
{
StartTime = original.StartTime,
Sample = original.Sample,
Position = ihp?.Position ?? Vector2.Zero,
ComboColour = ihco?.ComboColour ?? Color4.White,
ComboIndex = ihco?.ComboIndex ?? 0,
NewCombo = ihco?.NewCombo ?? false
};
}
private List<OsuHitObject> convertHitObjects(List<HitObject> hitObjects, float stackLeniency)
{
List<OsuHitObject> converted = new List<OsuHitObject>();
int combo = 0;
foreach (HitObject h in hitObjects)
{
if (h.NewCombo) combo = 0;
OsuHitObject c = convertHitObject(h);
h.ComboIndex = combo++;
if (c.NewCombo)
combo = 0;
c.ComboIndex = combo++;
converted.Add(h as OsuHitObject);
}
@ -62,9 +123,12 @@ namespace osu.Game.Modes.Osu.Beatmaps
if (stackBaseObject is Spinner) break;
OsuHitObject objectN = hitObjects[n];
if (objectN is Spinner) continue;
if (objectN is Spinner)
continue;
if (objectN.StartTime - stackBaseObject.EndTime > stackThreshold)
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;
@ -116,7 +180,9 @@ namespace osu.Game.Modes.Osu.Beatmaps
OsuHitObject objectN = hitObjects[n];
if (objectN is Spinner) continue;
if (objectI.StartTime - objectN.EndTime > stackThreshold)
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;

View File

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu.Objects.Drawables.Connections
{
@ -63,7 +64,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Connections
{
Vector2 startPosition = prevHitObject.EndPosition;
Vector2 endPosition = currHitObject.Position;
double startTime = prevHitObject.EndTime;
double startTime = (prevHitObject as IHasEndTime)?.EndTime ?? prevHitObject.StartTime;
double endTime = currHitObject.StartTime;
Vector2 distanceVector = endPosition - startPosition;

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu.Objects.Drawables
{
@ -35,11 +36,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{
glow = new GlowPiece
{
Colour = osuObject.Colour
Colour = osuObject.ComboColour
},
circle = new CirclePiece
{
Colour = osuObject.Colour,
Colour = osuObject.ComboColour,
Hit = () =>
{
if (Judgement.Result.HasValue) return false;
@ -57,11 +58,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
flash = new FlashPiece(),
explode = new ExplodePiece
{
Colour = osuObject.Colour,
Colour = osuObject.ComboColour,
},
ApproachCircle = new ApproachCircle
{
Colour = osuObject.Colour,
Colour = osuObject.ComboColour,
}
};
@ -118,13 +119,16 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ApproachCircle.FadeOut();
glow.Delay(osuObject.Duration);
double endTime = (osuObject as IHasEndTime)?.EndTime ?? osuObject.StartTime;
double duration = endTime - osuObject.StartTime;
glow.Delay(duration);
glow.FadeOut(400);
switch (state)
{
case ArmedState.Idle:
Delay(osuObject.Duration + TIME_PREEMPT);
Delay(duration + TIME_PREEMPT);
FadeOut(TIME_FADEOUT);
break;
case ArmedState.Miss:

View File

@ -60,7 +60,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
Position = s.StackedPosition,
ComboIndex = s.ComboIndex,
Scale = s.Scale,
Colour = s.Colour,
ComboColour = s.ComboColour,
Sample = s.Sample,
}),
};
@ -128,7 +128,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
var j = (OsuJudgementInfo)Judgement;
var sc = (OsuJudgementInfo)initialCircle.Judgement;
if (!userTriggered && Time.Current >= HitObject.EndTime)
if (!userTriggered && Time.Current >= slider.EndTime)
{
var ticksCount = ticks.Children.Count() + 1;
var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit);
@ -165,7 +165,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ball.FadeIn();
Delay(HitObject.Duration, true);
Delay(slider.Duration, true);
body.FadeOut(160);
ball.FadeOut(160);

View File

@ -47,7 +47,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = sliderTick.Colour,
Colour = sliderTick.ComboColour,
Alpha = 0.3f,
}
};

View File

@ -46,7 +46,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
Alpha = 0,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
DiscColour = s.Colour
DiscColour = s.ComboColour
},
circleContainer = new Container
{
@ -82,7 +82,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
if (Progress >= 1)
disc.Complete = true;
if (!userTriggered && Time.Current >= HitObject.EndTime)
if (!userTriggered && Time.Current >= spinner.EndTime)
{
if (Progress >= 1)
{
@ -102,7 +102,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
else
{
j.Score = OsuScoreResult.Miss;
if (Time.Current >= HitObject.EndTime)
if (Time.Current >= spinner.EndTime)
j.Result = HitResult.Miss;
}
}
@ -140,7 +140,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
base.UpdateState(state);
Delay(HitObject.Duration, true);
Delay(spinner.Duration, true);
FadeOut(160);

View File

@ -51,7 +51,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
new Box
{
Colour = slider.Colour,
Colour = slider.ComboColour,
Alpha = 0.4f,
Width = width,
Height = width,

View File

@ -110,10 +110,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{
progress -= border_portion;
bytes[i * 4] = (byte)(slider.Colour.R * 255);
bytes[i * 4 + 1] = (byte)(slider.Colour.G * 255);
bytes[i * 4 + 2] = (byte)(slider.Colour.B * 255);
bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.Colour.A * 255));
bytes[i * 4] = (byte)(slider.ComboColour.R * 255);
bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255);
bytes[i * 4 + 2] = (byte)(slider.ComboColour.B * 255);
bytes[i * 4 + 3] = (byte)((opacity_at_edge - (opacity_at_edge - opacity_at_centre) * progress / gradient_portion) * (slider.ComboColour.A * 255));
}
}

View File

@ -0,0 +1,10 @@
namespace osu.Game.Modes.Osu.Objects
{
public enum HitObjectType
{
Circle,
Slider,
Spinner,
SliderTick
}
}

View File

@ -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 System;
using osu.Game.Modes.Objects;
using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Game.Modes.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Objects
{
public abstract class OsuHitObject : HitObject
public abstract class OsuHitObject : HitObject, IHasCombo
{
public const double OBJECT_RADIUS = 64;
@ -36,6 +37,12 @@ namespace osu.Game.Modes.Osu.Objects
public abstract HitObjectType Type { get; }
public virtual bool NewCombo { get; set; }
public Color4 ComboColour { get; set; }
public int ComboIndex { get; set; }
public double HitWindowFor(OsuScoreResult result)
{
switch (result)
@ -69,16 +76,4 @@ namespace osu.Game.Modes.Osu.Objects
Scale = (1.0f - 0.7f * (beatmap.BeatmapInfo.BaseDifficulty.CircleSize - 5) / 5) / 2;
}
}
[Flags]
public enum HitObjectType
{
Circle = 1 << 0,
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
ColourHax = 122,
Hold = 1 << 7,
SliderTick = 1 << 8,
}
}

View File

@ -5,14 +5,16 @@ using OpenTK;
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing;
using osu.Game.Modes.Objects.Types;
using System;
using System.Collections.Generic;
namespace osu.Game.Modes.Osu.Objects
{
public class Slider : OsuHitObject
public class Slider : OsuHitObject, IHasEndTime
{
public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity;
public double EndTime => StartTime + RepeatCount * Curve.Length / Velocity;
public double Duration => EndTime - StartTime;
public override Vector2 EndPosition => PositionAt(1);
@ -64,7 +66,7 @@ namespace osu.Game.Modes.Osu.Objects
set { Curve.Length = value; }
}
public CurveTypes CurveType
public CurveType CurveType
{
get { return Curve.CurveType; }
set { Curve.CurveType = value; }
@ -124,7 +126,7 @@ namespace osu.Game.Modes.Osu.Objects
Position = Curve.PositionAt(distanceProgress),
StackHeight = StackHeight,
Scale = Scale,
Colour = Colour,
ComboColour = ComboColour,
Sample = new HitSampleInfo
{
Type = SampleType.None,
@ -138,12 +140,4 @@ namespace osu.Game.Modes.Osu.Objects
public override HitObjectType Type => HitObjectType.Slider;
}
public enum CurveTypes
{
Catmull,
Bezier,
Linear,
PerfectCurve
}
}

View File

@ -5,6 +5,7 @@ using System.Collections.Generic;
using OpenTK;
using System.Linq;
using osu.Framework.MathUtils;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu.Objects
{
@ -14,7 +15,7 @@ namespace osu.Game.Modes.Osu.Objects
public List<Vector2> ControlPoints;
public CurveTypes CurveType = CurveTypes.PerfectCurve;
public CurveType CurveType = CurveType.PerfectCurve;
public Vector2 Offset;
@ -25,9 +26,9 @@ namespace osu.Game.Modes.Osu.Objects
{
switch (CurveType)
{
case CurveTypes.Linear:
case CurveType.Linear:
return subControlPoints;
case CurveTypes.PerfectCurve:
case CurveType.PerfectCurve:
//we can only use CircularArc iff we have exactly three control points and no dissection.
if (ControlPoints.Count != 3 || subControlPoints.Count != 3)
break;

View File

@ -1,14 +1,18 @@
// 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.Osu.Objects
{
public class Spinner : OsuHitObject
public class Spinner : OsuHitObject, IHasEndTime
{
public double Length;
public override double EndTime => StartTime + Length;
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
public override HitObjectType Type => HitObjectType.Spinner;
public override bool NewCombo => true;
}
}

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics.Transforms;
using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Framework.MathUtils;
using System.Diagnostics;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu
{
@ -100,20 +101,22 @@ namespace osu.Game.Modes.Osu
{
OsuHitObject last = (OsuHitObject)beatmap.HitObjects[i - 1];
double endTime = (last as IHasEndTime)?.EndTime ?? last.StartTime;
//Make the cursor stay at a hitObject as long as possible (mainly for autopilot).
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
if (h.StartTime - h.HitWindowFor(OsuScoreResult.Miss) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Miss), h.Position.X, h.Position.Y, LegacyButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50) > endTime + h.HitWindowFor(OsuScoreResult.Hit50) + 50)
{
if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit50), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit50), h.Position.X, h.Position.Y, LegacyButtonState.None));
}
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
else if (h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100) > endTime + h.HitWindowFor(OsuScoreResult.Hit100) + 50)
{
if (!(last is Spinner) && h.StartTime - last.EndTime < 1000) addFrameToReplay(new LegacyReplayFrame(last.EndTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(last is Spinner) && h.StartTime - endTime < 1000) addFrameToReplay(new LegacyReplayFrame(endTime + h.HitWindowFor(OsuScoreResult.Hit100), last.EndPosition.X, last.EndPosition.Y, LegacyButtonState.None));
if (!(h is Spinner)) addFrameToReplay(new LegacyReplayFrame(h.StartTime - h.HitWindowFor(OsuScoreResult.Hit100), h.Position.X, h.Position.Y, LegacyButtonState.None));
}
}
@ -206,8 +209,10 @@ namespace osu.Game.Modes.Osu
LegacyButtonState button = buttonIndex % 2 == 0 ? LegacyButtonState.Left1 : LegacyButtonState.Right1;
double hEndTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
LegacyReplayFrame newFrame = new LegacyReplayFrame(h.StartTime, targetPosition.X, targetPosition.Y, button);
LegacyReplayFrame endFrame = new LegacyReplayFrame(h.EndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None);
LegacyReplayFrame endFrame = new LegacyReplayFrame(hEndTime + endDelay, h.EndPosition.X, h.EndPosition.Y, LegacyButtonState.None);
// Decrement because we want the previous frame, not the next one
int index = findInsertionIndex(newFrame) - 1;
@ -251,6 +256,8 @@ namespace osu.Game.Modes.Osu
// We add intermediate frames for spinning / following a slider here.
if (h is Spinner)
{
Spinner s = h as Spinner;
Vector2 difference = targetPosition - spinner_centre;
float radius = difference.Length;
@ -258,7 +265,7 @@ namespace osu.Game.Modes.Osu
double t;
for (double j = h.StartTime + frameDelay; j < h.EndTime; j += frameDelay)
for (double j = h.StartTime + frameDelay; j < s.EndTime; j += frameDelay)
{
t = applyModsToTime(j - h.StartTime) * spinnerDirection;
@ -266,10 +273,10 @@ namespace osu.Game.Modes.Osu
addFrameToReplay(new LegacyReplayFrame((int)j, pos.X, pos.Y, button));
}
t = applyModsToTime(h.EndTime - h.StartTime) * spinnerDirection;
t = applyModsToTime(s.EndTime - h.StartTime) * spinnerDirection;
Vector2 endPosition = spinner_centre + circlePosition(t / 20 + angle, spin_radius);
addFrameToReplay(new LegacyReplayFrame(h.EndTime, endPosition.X, endPosition.Y, button));
addFrameToReplay(new LegacyReplayFrame(s.EndTime, endPosition.X, endPosition.Y, button));
endFrame.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y;
@ -284,7 +291,7 @@ namespace osu.Game.Modes.Osu
addFrameToReplay(new LegacyReplayFrame(h.StartTime + j, pos.X, pos.Y, button));
}
addFrameToReplay(new LegacyReplayFrame(h.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
addFrameToReplay(new LegacyReplayFrame(s.EndTime, s.EndPosition.X, s.EndPosition.Y, button));
}
// We only want to let go of our button if we are at the end of the current replay. Otherwise something is still going on after us so we need to keep the button pressed!

View File

@ -4,7 +4,6 @@
using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.UI;
@ -94,8 +93,6 @@ namespace osu.Game.Modes.Osu
public override FontAwesome Icon => FontAwesome.fa_osu_osu_o;
public override HitObjectParser CreateHitObjectParser() => new OsuHitObjectParser();
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => new OsuScoreProcessor(hitObjectCount);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);

View File

@ -67,8 +67,8 @@
<Compile Include="Objects\Drawables\Pieces\TrianglesPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
<Compile Include="Objects\HitObjectType.cs" />
<Compile Include="Objects\OsuHitObjectDifficulty.cs" />
<Compile Include="Objects\OsuHitObjectParser.cs" />
<Compile Include="Objects\SliderCurve.cs" />
<Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuAutoReplay.cs" />

View File

@ -9,7 +9,7 @@ namespace osu.Game.Modes.Taiko.Beatmaps
{
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoBaseHit>
{
public Beatmap<TaikoBaseHit> Convert(Beatmap original)
public Beatmap<TaikoBaseHit> ConvertBeatmap(Beatmap original)
{
return new Beatmap<TaikoBaseHit>(original)
{

View File

@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Transforms;
using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Taiko.Objects.Drawable
{
@ -29,8 +30,11 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
{
Texture = textures.Get(@"Menu/logo");
double endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
double duration = endTime - h.StartTime;
Transforms.Add(new TransformPositionX { StartTime = h.StartTime - 200, EndTime = h.StartTime, StartValue = 1.1f, EndValue = 0.1f });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 });
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true);
}
}

View File

@ -1,39 +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 System.Collections.Generic;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Beatmaps;
namespace osu.Game.Modes.Taiko.Objects
{
internal class TaikoConverter : HitObjectConverter<TaikoBaseHit>
{
public override List<TaikoBaseHit> Convert(Beatmap beatmap)
{
List<TaikoBaseHit> output = new List<TaikoBaseHit>();
foreach (HitObject i in beatmap.HitObjects)
{
TaikoBaseHit h = i as TaikoBaseHit;
if (h == null)
{
OsuHitObject o = i as OsuHitObject;
if (o == null) throw new HitObjectConvertException(@"Taiko", i);
h = new TaikoBaseHit
{
StartTime = o.StartTime,
};
}
output.Add(h);
}
return output;
}
}
}

View File

@ -4,7 +4,6 @@
using OpenTK.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Taiko.UI;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
@ -89,8 +88,6 @@ namespace osu.Game.Modes.Taiko
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
}
}

View File

@ -51,7 +51,6 @@
<Compile Include="TaikoDifficultyCalculator.cs" />
<Compile Include="Objects\Drawable\DrawableTaikoHit.cs" />
<Compile Include="Objects\TaikoBaseHit.cs" />
<Compile Include="Objects\TaikoConverter.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\TaikoHitRenderer.cs" />
<Compile Include="UI\TaikoPlayfield.cs" />

View File

@ -33,7 +33,7 @@ namespace osu.Game.Beatmaps
protected DifficultyCalculator(Beatmap beatmap)
{
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
Objects = CreateBeatmapConverter().ConvertBeatmap(beatmap).HitObjects;
PreprocessHitObjects();
}

View File

@ -8,6 +8,7 @@ using osu.Game.Modes.Objects;
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing;
using osu.Game.Database;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Beatmaps.Formats
{
@ -81,8 +82,13 @@ namespace osu.Game.Beatmaps.Formats
foreach (HitObject h in b.HitObjects)
{
if (h.NewCombo || i == -1) i = (i + 1) % colours.Count;
h.Colour = colours[i];
IHasCombo ihc = h as IHasCombo;
if (ihc == null)
continue;
if (ihc.NewCombo || i == -1) i = (i + 1) % colours.Count;
ihc.ComboColour = colours[i];
}
}
}

View File

@ -265,7 +265,7 @@ namespace osu.Game.Beatmaps.Formats
{
case Section.General:
handleGeneral(beatmap, key, val);
parser = Ruleset.GetRuleset(beatmap.BeatmapInfo.Mode).CreateHitObjectParser();
parser = new LegacyHitObjectParser();
break;
case Section.Editor:
handleEditor(beatmap, key, val);

View File

@ -5,8 +5,17 @@ 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
{
Beatmap<T> Convert(Beatmap original);
/// <summary>
/// Converts a Beatmap to another mode.
/// </summary>
/// <param name="original">The original Beatmap.</param>
/// <returns>The converted Beatmap.</returns>
Beatmap<T> ConvertBeatmap(Beatmap original);
}
}

View File

@ -11,6 +11,7 @@ using osu.Framework.Audio.Sample;
using osu.Game.Beatmaps.Samples;
using OpenTK;
using Container = osu.Framework.Graphics.Containers.Container;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Objects.Drawables
{
@ -67,14 +68,14 @@ namespace osu.Game.Modes.Objects.Drawables
}
}
public abstract class DrawableHitObject<HitObjectType> : DrawableHitObject
where HitObjectType : HitObject
public abstract class DrawableHitObject<TObject> : DrawableHitObject
where TObject : HitObject
{
public event Action<DrawableHitObject<HitObjectType>, JudgementInfo> OnJudgement;
public event Action<DrawableHitObject<TObject>, JudgementInfo> OnJudgement;
public HitObjectType HitObject;
public TObject HitObject;
protected DrawableHitObject(HitObjectType hitObject)
protected DrawableHitObject(TObject hitObject)
{
HitObject = hitObject;
}
@ -88,7 +89,9 @@ namespace osu.Game.Modes.Objects.Drawables
if (Judgement.Result != null)
return false;
Judgement.TimeOffset = Time.Current - HitObject.EndTime;
double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
Judgement.TimeOffset = Time.Current - endTime;
CheckJudgement(userTriggered);
@ -138,14 +141,14 @@ namespace osu.Game.Modes.Objects.Drawables
Sample = audio.Sample.Get($@"Gameplay/{sampleSet.ToString().ToLower()}-hit{type.ToString().ToLower()}");
}
private List<DrawableHitObject<HitObjectType>> nestedHitObjects;
private List<DrawableHitObject<TObject>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<HitObjectType>> NestedHitObjects => nestedHitObjects;
protected IEnumerable<DrawableHitObject<TObject>> NestedHitObjects => nestedHitObjects;
protected void AddNested(DrawableHitObject<HitObjectType> h)
protected void AddNested(DrawableHitObject<TObject> h)
{
if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject<HitObjectType>>();
nestedHitObjects = new List<DrawableHitObject<TObject>>();
h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ;
nestedHitObjects.Add(h);

View File

@ -0,0 +1,15 @@
using osu.Game.Modes.Objects.Types;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Objects
{
internal class Hit : HitObject, IHasPosition, IHasCombo
{
public Vector2 Position { get; set; }
public Color4 ComboColour { get; set; }
public bool NewCombo { get; set; }
public int ComboIndex { get; set; }
}
}

View File

@ -3,28 +3,31 @@
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Samples;
using OpenTK.Graphics;
namespace osu.Game.Modes.Objects
{
/// <summary>
/// A hitobject describes a point in a beatmap
/// A HitObject describes an object in a Beatmap.
/// <para>
/// HitObjects may contain more properties for which you should be checking through the IHas* types.
/// </para>
/// </summary>
public abstract class HitObject
public class HitObject
{
public double StartTime;
public virtual double EndTime => StartTime;
/// <summary>
/// The time at which the HitObject starts.
/// </summary>
public double StartTime { get; set; }
public bool NewCombo { get; set; }
public Color4 Colour = new Color4(17, 136, 170, 255);
public double Duration => EndTime - StartTime;
public HitSampleInfo Sample;
public int ComboIndex;
/// <summary>
/// The sample to be played when this HitObject is hit.
/// </summary>
public HitSampleInfo Sample { get; set; }
/// <summary>
/// Sets default parameters from a beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to set from.</param>
public virtual void SetDefaultsFromBeatmap(Beatmap beatmap) { }
}
}

View File

@ -1,25 +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 System;
using System.Collections.Generic;
namespace osu.Game.Modes.Objects
{
public abstract class HitObjectConverter<T>
where T : HitObject
{
public abstract List<T> Convert(Beatmap beatmap);
}
public class HitObjectConvertException : Exception
{
public HitObject Input { get; }
public HitObjectConvertException(string modeName, HitObject input)
: base($@"Can't convert from {input.GetType().Name} to {modeName} HitObject!")
{
Input = input;
}
}
}

View File

@ -0,0 +1,17 @@
namespace osu.Game.Modes.Objects
{
/// <summary>
/// Converts HitObjects to another mode.
/// </summary>
/// <typeparam name="T">The type of HitObject to be converted to.</typeparam>
public interface IHitObjectConverter<T>
where T : HitObject
{
/// <summary>
/// Converts a <see cref="HitObject"/> to another mode.
/// </summary>
/// <param name="hitObject">The base HitObject to convert.</param>
/// <returns>The converted HitObject.</returns>
T Convert(HitObject hitObject);
}
}

View File

@ -1,16 +1,13 @@
// 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.Samples;
using osu.Game.Modes.Objects.Types;
using System;
using System.Collections.Generic;
using System.Globalization;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects;
using OpenTK;
namespace osu.Game.Modes.Osu.Objects
namespace osu.Game.Modes.Objects
{
public class OsuHitObjectParser : HitObjectParser
internal class LegacyHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text)
{
@ -19,17 +16,19 @@ namespace osu.Game.Modes.Osu.Objects
bool combo = type.HasFlag(HitObjectType.NewCombo);
type &= (HitObjectType)0xF;
type &= ~HitObjectType.NewCombo;
OsuHitObject result;
HitObject result;
switch (type)
{
case HitObjectType.Circle:
result = new HitCircle
result = new Hit
{
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1]))
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
NewCombo = combo
};
break;
case HitObjectType.Slider:
CurveTypes curveType = CurveTypes.Catmull;
CurveType curveType = CurveType.Catmull;
double length = 0;
List<Vector2> points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
@ -41,16 +40,16 @@ namespace osu.Game.Modes.Osu.Objects
switch (t)
{
case @"C":
curveType = CurveTypes.Catmull;
curveType = CurveType.Catmull;
break;
case @"B":
curveType = CurveTypes.Bezier;
curveType = CurveType.Bezier;
break;
case @"L":
curveType = CurveTypes.Linear;
curveType = CurveType.Linear;
break;
case @"P":
curveType = CurveTypes.PerfectCurve;
curveType = CurveType.PerfectCurve;
break;
}
continue;
@ -75,17 +74,17 @@ namespace osu.Game.Modes.Osu.Objects
result = new Slider
{
ControlPoints = points,
Length = length,
Distance = length,
CurveType = curveType,
RepeatCount = repeatCount,
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1]))
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
NewCombo = combo
};
break;
case HitObjectType.Spinner:
result = new Spinner
{
Length = Convert.ToDouble(split[5], CultureInfo.InvariantCulture) - Convert.ToDouble(split[2], CultureInfo.InvariantCulture),
Position = new Vector2(512, 384) / 2,
EndTime = Convert.ToDouble(split[5], CultureInfo.InvariantCulture)
};
break;
default:
@ -97,8 +96,9 @@ namespace osu.Game.Modes.Osu.Objects
Type = (SampleType)int.Parse(split[4]),
Set = SampleSet.Soft,
};
result.NewCombo = combo;
// TODO: "addition" field
return result;
}
}

View File

@ -1,13 +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
namespace osu.Game.Modes.Objects
{
/// <summary>
/// Returns null HitObjects but at least allows us to run.
/// </summary>
public class NullHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text) => null;
}
}

View File

@ -0,0 +1,23 @@
using osu.Game.Modes.Objects.Types;
using System.Collections.Generic;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Modes.Objects
{
internal class Slider : HitObject, IHasCurve, IHasDistance, IHasPosition, IHasCombo, IHasRepeats
{
public List<Vector2> ControlPoints { get; set; }
public CurveType CurveType { get; set; }
public double Distance { get; set; }
public Vector2 Position { get; set; }
public Color4 ComboColour { get; set; }
public bool NewCombo { get; set; }
public int ComboIndex { get; set; }
public int RepeatCount { get; set; }
}
}

View File

@ -0,0 +1,11 @@
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Objects
{
internal class Spinner : HitObject, IHasEndTime
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
}
}

View File

@ -0,0 +1,10 @@
namespace osu.Game.Modes.Objects.Types
{
public enum CurveType
{
Catmull,
Bezier,
Linear,
PerfectCurve
}
}

View File

@ -0,0 +1,25 @@
using OpenTK.Graphics;
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that is part of a combo.
/// </summary>
public interface IHasCombo
{
/// <summary>
/// The colour of this HitObject in the combo.
/// </summary>
Color4 ComboColour { get; set; }
/// <summary>
/// Whether the HitObject starts a new combo.
/// </summary>
bool NewCombo { get; }
/// <summary>
/// The combo index.
/// </summary>
int ComboIndex { get; }
}
}

View File

@ -0,0 +1,21 @@
using System.Collections.Generic;
using OpenTK;
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that has a curve.
/// </summary>
public interface IHasCurve
{
/// <summary>
/// The control points that shape the curve.
/// </summary>
List<Vector2> ControlPoints { get; }
/// <summary>
/// The type of curve.
/// </summary>
CurveType CurveType { get; }
}
}

View File

@ -0,0 +1,13 @@
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that has a distance.
/// </summary>
public interface IHasDistance
{
/// <summary>
/// The distance of the HitObject.
/// </summary>
double Distance { get; }
}
}

View File

@ -0,0 +1,18 @@
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that ends at a different time than its start time.
/// </summary>
public interface IHasEndTime
{
/// <summary>
/// The time at which the HitObject ends.
/// </summary>
double EndTime { get; }
/// <summary>
/// The duration of the HitObject.
/// </summary>
double Duration { get; }
}
}

View File

@ -0,0 +1,15 @@
using OpenTK;
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that has a starting position.
/// </summary>
public interface IHasPosition
{
/// <summary>
/// The starting position of the HitObject.
/// </summary>
Vector2 Position { get; }
}
}

View File

@ -0,0 +1,13 @@
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that spans some length.
/// </summary>
public interface IHasRepeats
{
/// <summary>
/// The amount of times the HitObject repeats.
/// </summary>
int RepeatCount { get; }
}
}

View File

@ -0,0 +1,16 @@
using System;
namespace osu.Game.Modes.Objects.Types
{
[Flags]
public enum HitObjectType
{
Circle = 1 << 0,
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
ColourHax = 122,
Hold = 1 << 7,
SliderTick = 1 << 8,
}
}

View File

@ -3,7 +3,6 @@
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI;
using osu.Game.Screens.Play;
using System;
@ -33,8 +32,6 @@ namespace osu.Game.Modes
public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap);
public abstract HitObjectParser CreateHitObjectParser();
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap);
public static void Register(Ruleset ruleset) => availableRulesets.TryAdd(ruleset.PlayMode, ruleset.GetType());

View File

@ -57,7 +57,7 @@ namespace osu.Game.Modes.UI
protected HitRenderer(Beatmap beatmap)
{
Beatmap = CreateBeatmapConverter().Convert(beatmap);
Beatmap = CreateBeatmapConverter().ConvertBeatmap(beatmap);
RelativeSizeAxes = Axes.Both;

View File

@ -21,6 +21,8 @@ using osu.Framework.MathUtils;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Modes;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Screens.Select
{
@ -69,11 +71,14 @@ namespace osu.Game.Screens.Select
if (beatmap.Beatmap != null)
{
HitObject lastObject = beatmap.Beatmap.HitObjects.Last();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
Icon = FontAwesome.fa_clock_o,
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(beatmap.Beatmap.HitObjects.Last().EndTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
Content = beatmap.Beatmap.HitObjects.Count == 0 ? "-" : TimeSpan.FromMilliseconds(endTime - beatmap.Beatmap.HitObjects.First().StartTime).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic

View File

@ -91,9 +91,21 @@
<Compile Include="IO\Legacy\SerializationWriter.cs" />
<Compile Include="IPC\ScoreIPCChannel.cs" />
<Compile Include="Modes\LegacyReplay.cs" />
<Compile Include="Modes\Objects\Hit.cs" />
<Compile Include="Modes\Objects\IHitObjectConverter.cs" />
<Compile Include="Modes\Objects\LegacyHitObjectParser.cs" />
<Compile Include="Modes\Objects\Slider.cs" />
<Compile Include="Modes\Objects\Spinner.cs" />
<Compile Include="Modes\Objects\Types\CurveType.cs" />
<Compile Include="Modes\Objects\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
<Compile Include="Modes\Objects\HitObjectParser.cs" />
<Compile Include="Modes\Objects\NullHitObjectParser.cs" />
<Compile Include="Modes\Objects\Types\IHasCombo.cs" />
<Compile Include="Modes\Objects\Types\IHasEndTime.cs" />
<Compile Include="Modes\Objects\Types\IHasDistance.cs" />
<Compile Include="Modes\Objects\Types\IHasCurve.cs" />
<Compile Include="Modes\Objects\Types\IHasRepeats.cs" />
<Compile Include="Modes\Objects\Types\IHasPosition.cs" />
<Compile Include="Modes\Objects\Types\LegacyHitObjectLive.cs" />
<Compile Include="Modes\Replay.cs" />
<Compile Include="Modes\Score.cs" />
<Compile Include="Modes\ScoreProcesssor.cs" />
@ -114,7 +126,6 @@
<Compile Include="Beatmaps\Drawables\Panel.cs" />
<Compile Include="Modes\Objects\Drawables\DrawableHitObject.cs" />
<Compile Include="Modes\Objects\HitObject.cs" />
<Compile Include="Modes\Objects\HitObjectConverter.cs" />
<Compile Include="Beatmaps\Samples\HitSampleInfo.cs" />
<Compile Include="Beatmaps\Samples\SampleBank.cs" />
<Compile Include="Beatmaps\Samples\SampleInfo.cs" />