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 add(new DrawableSpinner(new Spinner
{ {
StartTime = framedClock.CurrentTime + 600, StartTime = framedClock.CurrentTime + 600,
Length = 1000, EndTime = framedClock.CurrentTime + 1600,
Position = new Vector2(0, 0), Position = new Vector2(0, 0),
})); }));
break; break;

View File

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

View File

@ -5,7 +5,6 @@ using OpenTK.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Catch.UI; using osu.Game.Modes.Catch.UI;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Collections.Generic; using System.Collections.Generic;
@ -88,8 +87,6 @@ namespace osu.Game.Modes.Catch
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap); 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 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Catch.Objects 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.Textures;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using OpenTK; using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Catch.Objects.Drawable namespace osu.Game.Modes.Catch.Objects.Drawable
{ {
@ -21,7 +22,7 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
Origin = Anchor.Centre; Origin = Anchor.Centre;
Scale = new Vector2(0.1f); Scale = new Vector2(0.1f);
RelativePositionAxes = Axes.Y; RelativePositionAxes = Axes.Y;
Position = new Vector2(h.Position, -0.1f); Position = new Vector2(h.Position.X, -0.1f);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -29,8 +30,11 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); 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) }); double endTime = (h as IHasEndTime)?.EndTime ?? h.StartTime;
Transforms.Add(new TransformAlpha { StartTime = h.StartTime + h.Duration + 200, EndTime = h.StartTime + h.Duration + 400, StartValue = 1, EndValue = 0 }); 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); Expire(true);
} }
} }

View File

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

View File

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

View File

@ -4,7 +4,6 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Mania.UI; using osu.Game.Modes.Mania.UI;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System.Collections.Generic; using System.Collections.Generic;
@ -103,8 +102,6 @@ namespace osu.Game.Modes.Mania
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap); 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 // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Mania.Objects namespace osu.Game.Modes.Mania.Objects
{ {
public abstract class ManiaBaseHit : HitObject public abstract class ManiaBaseHit : HitObject, IHasEndTime
{ {
public int Column; 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\Drawable\DrawableNote.cs" />
<Compile Include="Objects\HoldNote.cs" /> <Compile Include="Objects\HoldNote.cs" />
<Compile Include="Objects\ManiaBaseHit.cs" /> <Compile Include="Objects\ManiaBaseHit.cs" />
<Compile Include="Objects\ManiaConverter.cs" />
<Compile Include="Objects\Note.cs" /> <Compile Include="Objects\Note.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\ManiaHitRenderer.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;
using osu.Game.Modes.Osu.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Modes.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Beatmaps namespace osu.Game.Modes.Osu.Beatmaps
{ {
internal class OsuBeatmapConverter : IBeatmapConverter<OsuHitObject> internal class OsuBeatmapConverter : IBeatmapConverter<OsuHitObject>
{ {
public Beatmap<OsuHitObject> Convert(Beatmap original) public Beatmap<OsuHitObject> ConvertBeatmap(Beatmap original)
{ {
return new Beatmap<OsuHitObject>(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>(); List<OsuHitObject> converted = new List<OsuHitObject>();
int combo = 0; int combo = 0;
foreach (HitObject h in hitObjects) 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); converted.Add(h as OsuHitObject);
} }
@ -62,9 +123,12 @@ namespace osu.Game.Modes.Osu.Beatmaps
if (stackBaseObject is Spinner) break; if (stackBaseObject is Spinner) break;
OsuHitObject objectN = hitObjects[n]; 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. //We are no longer within stacking range of the next object.
break; break;
@ -116,7 +180,9 @@ namespace osu.Game.Modes.Osu.Beatmaps
OsuHitObject objectN = hitObjects[n]; OsuHitObject objectN = hitObjects[n];
if (objectN is Spinner) continue; 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. //We are no longer within stacking range of the previous object.
break; break;

View File

@ -4,6 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using OpenTK; using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu.Objects.Drawables.Connections 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 startPosition = prevHitObject.EndPosition;
Vector2 endPosition = currHitObject.Position; Vector2 endPosition = currHitObject.Position;
double startTime = prevHitObject.EndTime; double startTime = (prevHitObject as IHasEndTime)?.EndTime ?? prevHitObject.StartTime;
double endTime = currHitObject.StartTime; double endTime = currHitObject.StartTime;
Vector2 distanceVector = endPosition - startPosition; 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.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables.Pieces; using osu.Game.Modes.Osu.Objects.Drawables.Pieces;
using OpenTK; using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu.Objects.Drawables namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
@ -35,11 +36,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
glow = new GlowPiece glow = new GlowPiece
{ {
Colour = osuObject.Colour Colour = osuObject.ComboColour
}, },
circle = new CirclePiece circle = new CirclePiece
{ {
Colour = osuObject.Colour, Colour = osuObject.ComboColour,
Hit = () => Hit = () =>
{ {
if (Judgement.Result.HasValue) return false; if (Judgement.Result.HasValue) return false;
@ -57,11 +58,11 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
flash = new FlashPiece(), flash = new FlashPiece(),
explode = new ExplodePiece explode = new ExplodePiece
{ {
Colour = osuObject.Colour, Colour = osuObject.ComboColour,
}, },
ApproachCircle = new ApproachCircle ApproachCircle = new ApproachCircle
{ {
Colour = osuObject.Colour, Colour = osuObject.ComboColour,
} }
}; };
@ -118,13 +119,16 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
ApproachCircle.FadeOut(); 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); glow.FadeOut(400);
switch (state) switch (state)
{ {
case ArmedState.Idle: case ArmedState.Idle:
Delay(osuObject.Duration + TIME_PREEMPT); Delay(duration + TIME_PREEMPT);
FadeOut(TIME_FADEOUT); FadeOut(TIME_FADEOUT);
break; break;
case ArmedState.Miss: case ArmedState.Miss:

View File

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

View File

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

View File

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

View File

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

View File

@ -110,10 +110,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces
{ {
progress -= border_portion; progress -= border_portion;
bytes[i * 4] = (byte)(slider.Colour.R * 255); bytes[i * 4] = (byte)(slider.ComboColour.R * 255);
bytes[i * 4 + 1] = (byte)(slider.Colour.G * 255); bytes[i * 4 + 1] = (byte)(slider.ComboColour.G * 255);
bytes[i * 4 + 2] = (byte)(slider.Colour.B * 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.Colour.A * 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>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using OpenTK; using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Game.Modes.Objects.Types;
using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Osu.Objects
{ {
public abstract class OsuHitObject : HitObject public abstract class OsuHitObject : HitObject, IHasCombo
{ {
public const double OBJECT_RADIUS = 64; public const double OBJECT_RADIUS = 64;
@ -36,6 +37,12 @@ namespace osu.Game.Modes.Osu.Objects
public abstract HitObjectType Type { get; } 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) public double HitWindowFor(OsuScoreResult result)
{ {
switch (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; 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;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Modes.Objects.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace osu.Game.Modes.Osu.Objects 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); public override Vector2 EndPosition => PositionAt(1);
@ -64,7 +66,7 @@ namespace osu.Game.Modes.Osu.Objects
set { Curve.Length = value; } set { Curve.Length = value; }
} }
public CurveTypes CurveType public CurveType CurveType
{ {
get { return Curve.CurveType; } get { return Curve.CurveType; }
set { Curve.CurveType = value; } set { Curve.CurveType = value; }
@ -124,7 +126,7 @@ namespace osu.Game.Modes.Osu.Objects
Position = Curve.PositionAt(distanceProgress), Position = Curve.PositionAt(distanceProgress),
StackHeight = StackHeight, StackHeight = StackHeight,
Scale = Scale, Scale = Scale,
Colour = Colour, ComboColour = ComboColour,
Sample = new HitSampleInfo Sample = new HitSampleInfo
{ {
Type = SampleType.None, Type = SampleType.None,
@ -138,12 +140,4 @@ namespace osu.Game.Modes.Osu.Objects
public override HitObjectType Type => HitObjectType.Slider; 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 OpenTK;
using System.Linq; using System.Linq;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Osu.Objects
{ {
@ -14,7 +15,7 @@ namespace osu.Game.Modes.Osu.Objects
public List<Vector2> ControlPoints; public List<Vector2> ControlPoints;
public CurveTypes CurveType = CurveTypes.PerfectCurve; public CurveType CurveType = CurveType.PerfectCurve;
public Vector2 Offset; public Vector2 Offset;
@ -25,9 +26,9 @@ namespace osu.Game.Modes.Osu.Objects
{ {
switch (CurveType) switch (CurveType)
{ {
case CurveTypes.Linear: case CurveType.Linear:
return subControlPoints; return subControlPoints;
case CurveTypes.PerfectCurve: case CurveType.PerfectCurve:
//we can only use CircularArc iff we have exactly three control points and no dissection. //we can only use CircularArc iff we have exactly three control points and no dissection.
if (ControlPoints.Count != 3 || subControlPoints.Count != 3) if (ControlPoints.Count != 3 || subControlPoints.Count != 3)
break; break;

View File

@ -1,14 +1,18 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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 namespace osu.Game.Modes.Osu.Objects
{ {
public class Spinner : OsuHitObject public class Spinner : OsuHitObject, IHasEndTime
{ {
public double Length; public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
public override double EndTime => StartTime + Length;
public override HitObjectType Type => HitObjectType.Spinner; 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.Game.Modes.Osu.Objects.Drawables;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using System.Diagnostics; using System.Diagnostics;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu
{ {
@ -100,20 +101,22 @@ namespace osu.Game.Modes.Osu
{ {
OsuHitObject last = (OsuHitObject)beatmap.HitObjects[i - 1]; 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). //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)); 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)); 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)); 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; 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 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 // Decrement because we want the previous frame, not the next one
int index = findInsertionIndex(newFrame) - 1; int index = findInsertionIndex(newFrame) - 1;
@ -251,6 +256,8 @@ namespace osu.Game.Modes.Osu
// We add intermediate frames for spinning / following a slider here. // We add intermediate frames for spinning / following a slider here.
if (h is Spinner) if (h is Spinner)
{ {
Spinner s = h as Spinner;
Vector2 difference = targetPosition - spinner_centre; Vector2 difference = targetPosition - spinner_centre;
float radius = difference.Length; float radius = difference.Length;
@ -258,7 +265,7 @@ namespace osu.Game.Modes.Osu
double t; 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; 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)); 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); 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.MouseX = endPosition.X;
endFrame.MouseY = endPosition.Y; 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.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! // 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 OpenTK.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Osu.UI;
using osu.Game.Modes.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 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 ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => new OsuScoreProcessor(hitObjectCount);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap); 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\TrianglesPiece.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBall.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBall.cs" />
<Compile Include="Objects\Drawables\Pieces\SliderBody.cs" /> <Compile Include="Objects\Drawables\Pieces\SliderBody.cs" />
<Compile Include="Objects\HitObjectType.cs" />
<Compile Include="Objects\OsuHitObjectDifficulty.cs" /> <Compile Include="Objects\OsuHitObjectDifficulty.cs" />
<Compile Include="Objects\OsuHitObjectParser.cs" />
<Compile Include="Objects\SliderCurve.cs" /> <Compile Include="Objects\SliderCurve.cs" />
<Compile Include="Objects\SliderTick.cs" /> <Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuAutoReplay.cs" /> <Compile Include="OsuAutoReplay.cs" />

View File

@ -9,7 +9,7 @@ namespace osu.Game.Modes.Taiko.Beatmaps
{ {
internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoBaseHit> internal class TaikoBeatmapConverter : IBeatmapConverter<TaikoBaseHit>
{ {
public Beatmap<TaikoBaseHit> Convert(Beatmap original) public Beatmap<TaikoBaseHit> ConvertBeatmap(Beatmap original)
{ {
return new Beatmap<TaikoBaseHit>(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.Textures;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using OpenTK; using OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Taiko.Objects.Drawable namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
@ -29,8 +30,11 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); 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 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); 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 OpenTK.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.Taiko.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
@ -89,8 +88,6 @@ namespace osu.Game.Modes.Taiko
public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null; public override ScoreProcessor CreateScoreProcessor(int hitObjectCount = 0) => null;
public override HitObjectParser CreateHitObjectParser() => new NullHitObjectParser();
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
} }
} }

View File

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

View File

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

View File

@ -8,6 +8,7 @@ using osu.Game.Modes.Objects;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Beatmaps.Formats namespace osu.Game.Beatmaps.Formats
{ {
@ -81,8 +82,13 @@ namespace osu.Game.Beatmaps.Formats
foreach (HitObject h in b.HitObjects) foreach (HitObject h in b.HitObjects)
{ {
if (h.NewCombo || i == -1) i = (i + 1) % colours.Count; IHasCombo ihc = h as IHasCombo;
h.Colour = colours[i];
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: case Section.General:
handleGeneral(beatmap, key, val); handleGeneral(beatmap, key, val);
parser = Ruleset.GetRuleset(beatmap.BeatmapInfo.Mode).CreateHitObjectParser(); parser = new LegacyHitObjectParser();
break; break;
case Section.Editor: case Section.Editor:
handleEditor(beatmap, key, val); handleEditor(beatmap, key, val);

View File

@ -5,8 +5,17 @@ using osu.Game.Modes.Objects;
namespace osu.Game.Beatmaps 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 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 osu.Game.Beatmaps.Samples;
using OpenTK; using OpenTK;
using Container = osu.Framework.Graphics.Containers.Container; using Container = osu.Framework.Graphics.Containers.Container;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Objects.Drawables namespace osu.Game.Modes.Objects.Drawables
{ {
@ -67,14 +68,14 @@ namespace osu.Game.Modes.Objects.Drawables
} }
} }
public abstract class DrawableHitObject<HitObjectType> : DrawableHitObject public abstract class DrawableHitObject<TObject> : DrawableHitObject
where HitObjectType : HitObject 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; HitObject = hitObject;
} }
@ -88,7 +89,9 @@ namespace osu.Game.Modes.Objects.Drawables
if (Judgement.Result != null) if (Judgement.Result != null)
return false; return false;
Judgement.TimeOffset = Time.Current - HitObject.EndTime; double endTime = (HitObject as IHasEndTime)?.EndTime ?? HitObject.StartTime;
Judgement.TimeOffset = Time.Current - endTime;
CheckJudgement(userTriggered); CheckJudgement(userTriggered);
@ -138,14 +141,14 @@ namespace osu.Game.Modes.Objects.Drawables
Sample = audio.Sample.Get($@"Gameplay/{sampleSet.ToString().ToLower()}-hit{type.ToString().ToLower()}"); 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) if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject<HitObjectType>>(); nestedHitObjects = new List<DrawableHitObject<TObject>>();
h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ; h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ;
nestedHitObjects.Add(h); 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;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
using OpenTK.Graphics;
namespace osu.Game.Modes.Objects namespace osu.Game.Modes.Objects
{ {
/// <summary> /// <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> /// </summary>
public abstract class HitObject public class HitObject
{ {
public double StartTime; /// <summary>
public virtual double EndTime => StartTime; /// The time at which the HitObject starts.
/// </summary>
public double StartTime { get; set; }
public bool NewCombo { get; set; } /// <summary>
/// The sample to be played when this HitObject is hit.
public Color4 Colour = new Color4(17, 136, 170, 255); /// </summary>
public HitSampleInfo Sample { get; set; }
public double Duration => EndTime - StartTime;
public HitSampleInfo Sample;
public int ComboIndex;
/// <summary>
/// Sets default parameters from a beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to set from.</param>
public virtual void SetDefaultsFromBeatmap(Beatmap beatmap) { } 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>. using OpenTK;
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects.Types;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; 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) public override HitObject Parse(string text)
{ {
@ -19,17 +16,19 @@ namespace osu.Game.Modes.Osu.Objects
bool combo = type.HasFlag(HitObjectType.NewCombo); bool combo = type.HasFlag(HitObjectType.NewCombo);
type &= (HitObjectType)0xF; type &= (HitObjectType)0xF;
type &= ~HitObjectType.NewCombo; type &= ~HitObjectType.NewCombo;
OsuHitObject result;
HitObject result;
switch (type) switch (type)
{ {
case HitObjectType.Circle: 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; break;
case HitObjectType.Slider: case HitObjectType.Slider:
CurveTypes curveType = CurveTypes.Catmull; CurveType curveType = CurveType.Catmull;
double length = 0; double length = 0;
List<Vector2> points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) }; 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) switch (t)
{ {
case @"C": case @"C":
curveType = CurveTypes.Catmull; curveType = CurveType.Catmull;
break; break;
case @"B": case @"B":
curveType = CurveTypes.Bezier; curveType = CurveType.Bezier;
break; break;
case @"L": case @"L":
curveType = CurveTypes.Linear; curveType = CurveType.Linear;
break; break;
case @"P": case @"P":
curveType = CurveTypes.PerfectCurve; curveType = CurveType.PerfectCurve;
break; break;
} }
continue; continue;
@ -75,17 +74,17 @@ namespace osu.Game.Modes.Osu.Objects
result = new Slider result = new Slider
{ {
ControlPoints = points, ControlPoints = points,
Length = length, Distance = length,
CurveType = curveType, CurveType = curveType,
RepeatCount = repeatCount, 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; break;
case HitObjectType.Spinner: case HitObjectType.Spinner:
result = new Spinner result = new Spinner
{ {
Length = Convert.ToDouble(split[5], CultureInfo.InvariantCulture) - Convert.ToDouble(split[2], CultureInfo.InvariantCulture), EndTime = Convert.ToDouble(split[5], CultureInfo.InvariantCulture)
Position = new Vector2(512, 384) / 2,
}; };
break; break;
default: default:
@ -97,8 +96,9 @@ namespace osu.Game.Modes.Osu.Objects
Type = (SampleType)int.Parse(split[4]), Type = (SampleType)int.Parse(split[4]),
Set = SampleSet.Soft, Set = SampleSet.Soft,
}; };
result.NewCombo = combo;
// TODO: "addition" field // TODO: "addition" field
return result; 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.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Modes.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System; using System;
@ -33,8 +32,6 @@ namespace osu.Game.Modes
public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap); public abstract HitRenderer CreateHitRendererWith(Beatmap beatmap);
public abstract HitObjectParser CreateHitObjectParser();
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap); public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap);
public static void Register(Ruleset ruleset) => availableRulesets.TryAdd(ruleset.PlayMode, ruleset.GetType()); 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) protected HitRenderer(Beatmap beatmap)
{ {
Beatmap = CreateBeatmapConverter().Convert(beatmap); Beatmap = CreateBeatmapConverter().ConvertBeatmap(beatmap);
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;

View File

@ -21,6 +21,8 @@ using osu.Framework.MathUtils;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Screens.Select namespace osu.Game.Screens.Select
{ {
@ -69,11 +71,14 @@ namespace osu.Game.Screens.Select
if (beatmap.Beatmap != null) if (beatmap.Beatmap != null)
{ {
HitObject lastObject = beatmap.Beatmap.HitObjects.Last();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject.StartTime;
labels.Add(new InfoLabel(new BeatmapStatistic labels.Add(new InfoLabel(new BeatmapStatistic
{ {
Name = "Length", Name = "Length",
Icon = FontAwesome.fa_clock_o, 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 labels.Add(new InfoLabel(new BeatmapStatistic

View File

@ -91,9 +91,21 @@
<Compile Include="IO\Legacy\SerializationWriter.cs" /> <Compile Include="IO\Legacy\SerializationWriter.cs" />
<Compile Include="IPC\ScoreIPCChannel.cs" /> <Compile Include="IPC\ScoreIPCChannel.cs" />
<Compile Include="Modes\LegacyReplay.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\Drawables\IDrawableHitObjectWithProxiedApproach.cs" />
<Compile Include="Modes\Objects\HitObjectParser.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\Replay.cs" />
<Compile Include="Modes\Score.cs" /> <Compile Include="Modes\Score.cs" />
<Compile Include="Modes\ScoreProcesssor.cs" /> <Compile Include="Modes\ScoreProcesssor.cs" />
@ -114,7 +126,6 @@
<Compile Include="Beatmaps\Drawables\Panel.cs" /> <Compile Include="Beatmaps\Drawables\Panel.cs" />
<Compile Include="Modes\Objects\Drawables\DrawableHitObject.cs" /> <Compile Include="Modes\Objects\Drawables\DrawableHitObject.cs" />
<Compile Include="Modes\Objects\HitObject.cs" /> <Compile Include="Modes\Objects\HitObject.cs" />
<Compile Include="Modes\Objects\HitObjectConverter.cs" />
<Compile Include="Beatmaps\Samples\HitSampleInfo.cs" /> <Compile Include="Beatmaps\Samples\HitSampleInfo.cs" />
<Compile Include="Beatmaps\Samples\SampleBank.cs" /> <Compile Include="Beatmaps\Samples\SampleBank.cs" />
<Compile Include="Beatmaps\Samples\SampleInfo.cs" /> <Compile Include="Beatmaps\Samples\SampleInfo.cs" />