1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-19 16:22:54 +08:00

Merge remote-tracking branch 'origin/master' into generic_judgements_2

Conflicts:
	osu.Desktop.VisualTests/Tests/TestCaseHitObjects.cs
	osu.Game.Modes.Catch/UI/CatchHitRenderer.cs
	osu.Game.Modes.Mania/UI/ManiaHitRenderer.cs
	osu.Game.Modes.Mania/osu.Game.Modes.Mania.csproj
	osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs
	osu.Game.Modes.Osu/Objects/Drawables/DrawableSpinner.cs
	osu.Game.Modes.Osu/UI/OsuHitRenderer.cs
	osu.Game.Modes.Taiko/UI/TaikoHitRenderer.cs
	osu.Game.Modes.Taiko/osu.Game.Modes.Taiko.csproj
	osu.Game/Modes/Objects/Drawables/DrawableHitObject.cs
	osu.Game/Modes/UI/HitRenderer.cs
	osu.Game/osu.Game.csproj
This commit is contained in:
smoogipooo 2017-03-15 21:36:43 +09:00
commit 42da0f1a72
70 changed files with 845 additions and 371 deletions

@ -1 +1 @@
Subproject commit 8c79c94aa71711cf9c8bb169f684df2e35596f9d Subproject commit 036b124eb2387dde29af56e06391c42063ec85e2

View File

@ -48,6 +48,7 @@ namespace osu.Desktop.VisualTests.Tests
HitObjects = objects, HitObjects = objects,
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BaseDifficulty(),
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
Artist = @"Unknown", Artist = @"Unknown",

View File

@ -1,20 +1,21 @@
// 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.Collections.Generic;
using osu.Framework.Screens.Testing;
using osu.Framework.Graphics;
using osu.Framework.Timing;
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Game.Modes.Objects.Drawables; using osu.Framework.Graphics;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.Objects.Drawables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Modes.Osu.Judgements; using osu.Game.Modes.Osu.Judgements;
using OpenTK.Graphics; using osu.Framework.Screens.Testing;
using osu.Framework.Timing;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.Objects.Drawables;
using System.Collections.Generic;
namespace osu.Desktop.VisualTests.Tests namespace osu.Desktop.VisualTests.Tests
{ {
@ -61,12 +62,15 @@ namespace osu.Desktop.VisualTests.Tests
add(new DrawableSlider(new Slider add(new DrawableSlider(new Slider
{ {
StartTime = framedClock.CurrentTime + 600, StartTime = framedClock.CurrentTime + 600,
CurveObject = new CurvedHitObject
{
ControlPoints = new List<Vector2> ControlPoints = new List<Vector2>
{ {
new Vector2(-200, 0), new Vector2(-200, 0),
new Vector2(400, 0), new Vector2(400, 0),
}, },
Length = 400, Distance = 400
},
Position = new Vector2(-200, 0), Position = new Vector2(-200, 0),
Velocity = 1, Velocity = 1,
TickDistance = 100, TickDistance = 100,
@ -76,7 +80,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

@ -6,7 +6,6 @@ using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Screens.Testing; using osu.Framework.Screens.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats;
using OpenTK; using OpenTK;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps.IO; using osu.Game.Beatmaps.IO;
@ -61,13 +60,12 @@ namespace osu.Desktop.VisualTests.Tests
time += 500; time += 500;
} }
var decoder = new ConstructableBeatmapDecoder();
Beatmap b = new Beatmap Beatmap b = new Beatmap
{ {
HitObjects = objects, HitObjects = objects,
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
BaseDifficulty = new BaseDifficulty(),
Metadata = new BeatmapMetadata Metadata = new BeatmapMetadata
{ {
Artist = @"Unknown", Artist = @"Unknown",
@ -77,8 +75,6 @@ namespace osu.Desktop.VisualTests.Tests
} }
}; };
decoder.Process(b);
beatmap = new TestWorkingBeatmap(b); beatmap = new TestWorkingBeatmap(b);
} }

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
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

View File

@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Modes.Catch.Objects;
namespace osu.Game.Modes.Catch.Beatmaps
{
internal class CatchBeatmapProcessor : IBeatmapProcessor<CatchBaseHit>
{
public void SetDefaults(CatchBaseHit hitObject, Beatmap<CatchBaseHit> beatmap)
{
}
public void PostProcess(Beatmap<CatchBaseHit> beatmap)
{
}
}
}

View File

@ -7,7 +7,6 @@ using osu.Game.Graphics;
using osu.Game.Modes.Catch.Mods; using osu.Game.Modes.Catch.Mods;
using osu.Game.Modes.Catch.UI; using osu.Game.Modes.Catch.UI;
using osu.Game.Modes.Mods; using osu.Game.Modes.Mods;
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;
@ -90,8 +89,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

@ -7,6 +7,6 @@ namespace osu.Game.Modes.Catch.Objects
{ {
public abstract class CatchBaseHit : HitObject public abstract class CatchBaseHit : HitObject
{ {
public float Position; public float Position { get; set; }
} }
} }

View File

@ -29,8 +29,10 @@ namespace osu.Game.Modes.Catch.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); Texture = textures.Get(@"Menu/logo");
double duration = 0;
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 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 }); Transforms.Add(new TransformAlpha { StartTime = h.StartTime + duration + 200, EndTime = h.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true); Expire(true);
} }
} }

View File

@ -19,8 +19,10 @@ namespace osu.Game.Modes.Catch.UI
protected override IBeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter(); protected override IBeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override IBeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override Playfield<CatchBaseHit, CatchJudgementInfo> CreatePlayfield() => new CatchPlayfield(); protected override Playfield<CatchBaseHit, CatchJudgementInfo> CreatePlayfield() => new CatchPlayfield();
protected override DrawableHitObject<CatchBaseHit, CatchJudgementInfo> GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h); protected override DrawableHitObject<CatchBaseHit, CatchJudgementInfo> GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h);
} }
} }

View File

@ -48,6 +48,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\CatchBeatmapConverter.cs" /> <Compile Include="Beatmaps\CatchBeatmapConverter.cs" />
<Compile Include="Beatmaps\CatchBeatmapProcessor.cs" />
<Compile Include="CatchDifficultyCalculator.cs" /> <Compile Include="CatchDifficultyCalculator.cs" />
<Compile Include="Judgements\CatchJudgementInfo.cs" /> <Compile Include="Judgements\CatchJudgementInfo.cs" />
<Compile Include="Objects\CatchBaseHit.cs" /> <Compile Include="Objects\CatchBaseHit.cs" />

View File

@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Modes.Mania.Objects;
namespace osu.Game.Modes.Mania.Beatmaps
{
internal class ManiaBeatmapProcessor : IBeatmapProcessor<ManiaBaseHit>
{
public void SetDefaults(ManiaBaseHit hitObject, Beatmap<ManiaBaseHit> beatmap)
{
}
public void PostProcess(Beatmap<ManiaBaseHit> beatmap)
{
}
}
}

View File

@ -6,7 +6,6 @@ using osu.Game.Graphics;
using osu.Game.Modes.Mania.Mods; using osu.Game.Modes.Mania.Mods;
using osu.Game.Modes.Mania.UI; using osu.Game.Modes.Mania.UI;
using osu.Game.Modes.Mods; using osu.Game.Modes.Mods;
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;
@ -105,8 +104,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

@ -26,8 +26,10 @@ namespace osu.Game.Modes.Mania.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); Texture = textures.Get(@"Menu/logo");
double duration = 0;
Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f }); Transforms.Add(new TransformPositionY { StartTime = note.StartTime - 200, EndTime = note.StartTime, StartValue = -0.1f, EndValue = 0.9f });
Transforms.Add(new TransformAlpha { StartTime = note.StartTime + note.Duration + 200, EndTime = note.StartTime + note.Duration + 400, StartValue = 1, EndValue = 0 }); Transforms.Add(new TransformAlpha { StartTime = note.StartTime + duration + 200, EndTime = note.StartTime + duration + 400, StartValue = 1, EndValue = 0 });
Expire(true); Expire(true);
} }
} }

View File

@ -22,8 +22,10 @@ namespace osu.Game.Modes.Mania.UI
protected override IBeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override IBeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override IBeatmapProcessor<ManiaBaseHit> CreateBeatmapProcessor() => new ManiaBeatmapProcessor();
protected override Playfield<ManiaBaseHit, ManiaJudgementInfo> CreatePlayfield() => new ManiaPlayfield(columns); protected override Playfield<ManiaBaseHit, ManiaJudgementInfo> CreatePlayfield() => new ManiaPlayfield(columns);
protected override DrawableHitObject<ManiaBaseHit, ManiaJudgementInfo> GetVisualRepresentation(ManiaBaseHit h) protected override DrawableHitObject<ManiaBaseHit, ManiaJudgementInfo> GetVisualRepresentation(ManiaBaseHit h)
{ {
return null; return null;

View File

@ -48,6 +48,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\ManiaBeatmapConverter.cs" /> <Compile Include="Beatmaps\ManiaBeatmapConverter.cs" />
<Compile Include="Beatmaps\ManiaBeatmapProcessor.cs" />
<Compile Include="Judgements\ManiaJudgementInfo.cs" /> <Compile Include="Judgements\ManiaJudgementInfo.cs" />
<Compile Include="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Objects\Drawable\DrawableNote.cs" /> <Compile Include="Objects\Drawable\DrawableNote.cs" />

View File

@ -7,6 +7,8 @@ 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 System.Linq;
namespace osu.Game.Modes.Osu.Beatmaps namespace osu.Game.Modes.Osu.Beatmaps
{ {
@ -16,28 +18,64 @@ namespace osu.Game.Modes.Osu.Beatmaps
{ {
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 List<OsuHitObject> convertHitObjects(List<HitObject> hitObjects, float stackLeniency)
{ {
List<OsuHitObject> converted = new List<OsuHitObject>(); List<OsuHitObject> converted = hitObjects.Select(convertHitObject).ToList();
int combo = 0;
foreach (HitObject h in hitObjects)
{
if (h.NewCombo) combo = 0;
h.ComboIndex = combo++;
converted.Add(h as OsuHitObject);
}
updateStacking(converted, stackLeniency); updateStacking(converted, stackLeniency);
return converted; return converted;
} }
private OsuHitObject convertHitObject(HitObject original)
{
IHasCurve curveData = original as IHasCurve;
IHasEndTime endTimeData = original as IHasEndTime;
IHasPosition positionData = original as IHasPosition;
IHasCombo comboData = original as IHasCombo;
if (curveData != null)
{
return new Slider
{
StartTime = original.StartTime,
Sample = original.Sample,
CurveObject = curveData,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false
};
}
if (endTimeData != null)
{
return new Spinner
{
StartTime = original.StartTime,
Sample = original.Sample,
Position = new Vector2(512, 384) / 2,
EndTime = endTimeData.EndTime
};
}
return new HitCircle
{
StartTime = original.StartTime,
Sample = original.Sample,
Position = positionData?.Position ?? Vector2.Zero,
NewCombo = comboData?.NewCombo ?? false
};
}
private void updateStacking(List<OsuHitObject> hitObjects, float stackLeniency, int startIndex = 0, int endIndex = -1) private void updateStacking(List<OsuHitObject> hitObjects, float stackLeniency, int startIndex = 0, int endIndex = -1)
{ {
if (endIndex == -1) if (endIndex == -1)
@ -61,9 +99,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;
@ -115,7 +156,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

@ -0,0 +1,37 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Modes.Osu.Objects;
namespace osu.Game.Modes.Osu.Beatmaps
{
internal class OsuBeatmapProcessor : IBeatmapProcessor<OsuHitObject>
{
public void SetDefaults(OsuHitObject hitObject, Beatmap<OsuHitObject> beatmap)
{
hitObject.SetDefaultsFromBeatmap(beatmap);
}
public void PostProcess(Beatmap<OsuHitObject> beatmap)
{
if (beatmap.ComboColors.Count == 0)
return;
int comboIndex = 0;
int colourIndex = 0;
foreach (var obj in beatmap.HitObjects)
{
if (obj.NewCombo)
{
comboIndex = 0;
colourIndex = (colourIndex + 1) % beatmap.ComboColors.Count;
}
obj.ComboIndex = comboIndex++;
obj.ComboColour = beatmap.ComboColors[colourIndex];
}
}
}
}

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,
} }
}; };
@ -116,13 +117,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,
}), }),
}; };
@ -72,7 +72,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
AddNested(initialCircle); AddNested(initialCircle);
var repeatDuration = s.Curve.Length / s.Velocity; var repeatDuration = s.Curve.Distance / s.Velocity;
foreach (var tick in s.Ticks) foreach (var tick in s.Ticks)
{ {
var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration; var repeatStartTime = s.StartTime + tick.RepeatIndex * repeatDuration;
@ -104,7 +104,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1);
int repeat = slider.RepeatAt(progress); int repeat = slider.RepeatAt(progress);
progress = slider.CurveProgressAt(progress); progress = slider.ProgressAt(progress);
if (repeat > currentRepeat) if (repeat > currentRepeat)
{ {
@ -125,7 +125,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
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);
@ -162,7 +162,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

@ -48,7 +48,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
{ {
@ -80,7 +80,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)
{ {
@ -100,7 +100,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
else else
{ {
Judgement.Score = OsuScoreResult.Miss; Judgement.Score = OsuScoreResult.Miss;
if (Time.Current >= HitObject.EndTime) if (Time.Current >= spinner.EndTime)
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
} }
} }
@ -138,7 +138,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,13 @@
// 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.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, IHasPosition
{ {
public const double OBJECT_RADIUS = 64; public const double OBJECT_RADIUS = 64;
@ -36,6 +37,10 @@ namespace osu.Game.Modes.Osu.Objects
public abstract HitObjectType Type { get; } public abstract HitObjectType Type { get; }
public Color4 ComboColour { get; set; }
public virtual bool NewCombo { get; set; }
public int ComboIndex { get; set; }
public double HitWindowFor(OsuScoreResult result) public double HitWindowFor(OsuScoreResult result)
{ {
switch (result) switch (result)
@ -62,23 +67,9 @@ namespace osu.Game.Modes.Osu.Objects
return OsuScoreResult.Miss; return OsuScoreResult.Miss;
} }
public override void SetDefaultsFromBeatmap(Beatmap beatmap) public virtual void SetDefaultsFromBeatmap(Beatmap<OsuHitObject> beatmap)
{ {
base.SetDefaultsFromBeatmap(beatmap);
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

@ -1,105 +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 System.Globalization;
using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects;
using OpenTK;
namespace osu.Game.Modes.Osu.Objects
{
public class OsuHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text)
{
string[] split = text.Split(',');
var type = (HitObjectType)int.Parse(split[3]);
bool combo = type.HasFlag(HitObjectType.NewCombo);
type &= (HitObjectType)0xF;
type &= ~HitObjectType.NewCombo;
OsuHitObject result;
switch (type)
{
case HitObjectType.Circle:
result = new HitCircle
{
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1]))
};
break;
case HitObjectType.Slider:
CurveTypes curveType = CurveTypes.Catmull;
double length = 0;
List<Vector2> points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
string[] pointsplit = split[5].Split('|');
foreach (string t in pointsplit)
{
if (t.Length == 1)
{
switch (t)
{
case @"C":
curveType = CurveTypes.Catmull;
break;
case @"B":
curveType = CurveTypes.Bezier;
break;
case @"L":
curveType = CurveTypes.Linear;
break;
case @"P":
curveType = CurveTypes.PerfectCurve;
break;
}
continue;
}
string[] temp = t.Split(':');
Vector2 v = new Vector2(
(int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture),
(int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)
);
points.Add(v);
}
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
if (repeatCount > 9000)
throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
result = new Slider
{
ControlPoints = points,
Length = length,
CurveType = curveType,
RepeatCount = repeatCount,
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1]))
};
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,
};
break;
default:
throw new InvalidOperationException($@"Unknown hit object type {type}");
}
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
result.Sample = new HitSampleInfo
{
Type = (SampleType)int.Parse(split[4]),
Set = SampleSet.Soft,
};
result.NewCombo = combo;
// TODO: "addition" field
return result;
}
}
}

View File

@ -5,41 +5,33 @@ 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;
using osu.Game.Modes.Objects;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Osu.Objects
{ {
public class Slider : OsuHitObject public class Slider : OsuHitObject, IHasCurve
{ {
public override double EndTime => StartTime + RepeatCount * Curve.Length / Velocity; public IHasCurve CurveObject { get; set; }
public SliderCurve Curve => CurveObject.Curve;
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
public double Duration => EndTime - StartTime;
public override Vector2 EndPosition => PositionAt(1); public override Vector2 EndPosition => PositionAt(1);
/// <summary> public Vector2 PositionAt(double progress) => CurveObject.PositionAt(progress);
/// Computes the position on the slider at a given progress that ranges from 0 (beginning of the slider) public double ProgressAt(double progress) => CurveObject.ProgressAt(progress);
/// to 1 (end of the slider). This includes repeat logic. public int RepeatAt(double progress) => CurveObject.RepeatAt(progress);
/// </summary>
/// <param name="progress">Ranges from 0 (beginning of the slider) to 1 (end of the slider).</param>
/// <returns></returns>
public Vector2 PositionAt(double progress) => Curve.PositionAt(CurveProgressAt(progress));
/// <summary> public List<Vector2> ControlPoints => CurveObject.ControlPoints;
/// Find the current progress along the curve, accounting for repeat logic. public CurveType CurveType => CurveObject.CurveType;
/// </summary> public double Distance => CurveObject.Distance;
public double CurveProgressAt(double progress)
{
var p = progress * RepeatCount % 1;
if (RepeatAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
/// <summary> public int RepeatCount => CurveObject.RepeatCount;
/// Determine which repeat of the slider we are on at a given progress.
/// Range is 0..RepeatCount where 0 is the first run.
/// </summary>
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
private int stackHeight; private int stackHeight;
public override int StackHeight public override int StackHeight
@ -52,28 +44,10 @@ namespace osu.Game.Modes.Osu.Objects
} }
} }
public List<Vector2> ControlPoints
{
get { return Curve.ControlPoints; }
set { Curve.ControlPoints = value; }
}
public double Length
{
get { return Curve.Length; }
set { Curve.Length = value; }
}
public CurveTypes CurveType
{
get { return Curve.CurveType; }
set { Curve.CurveType = value; }
}
public double Velocity; public double Velocity;
public double TickDistance; public double TickDistance;
public override void SetDefaultsFromBeatmap(Beatmap beatmap) public override void SetDefaultsFromBeatmap(Beatmap<OsuHitObject> beatmap)
{ {
base.SetDefaultsFromBeatmap(beatmap); base.SetDefaultsFromBeatmap(beatmap);
@ -88,17 +62,13 @@ namespace osu.Game.Modes.Osu.Objects
TickDistance = baseVelocity / baseDifficulty.SliderTickRate; TickDistance = baseVelocity / baseDifficulty.SliderTickRate;
} }
public int RepeatCount = 1;
internal readonly SliderCurve Curve = new SliderCurve();
public IEnumerable<SliderTick> Ticks public IEnumerable<SliderTick> Ticks
{ {
get get
{ {
if (TickDistance == 0) yield break; if (TickDistance == 0) yield break;
var length = Curve.Length; var length = Curve.Distance;
var tickDistance = Math.Min(TickDistance, length); var tickDistance = Math.Min(TickDistance, length);
var repeatDuration = length / Velocity; var repeatDuration = length / Velocity;
@ -124,7 +94,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 +108,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

@ -1,14 +1,17 @@
// 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.Game.Modes.Osu.Objects.Drawables;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
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 = beatmap.HitObjects[i - 1]; OsuHitObject last = 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

@ -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.Mods; using osu.Game.Modes.Mods;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Osu.Mods; using osu.Game.Modes.Osu.Mods;
using osu.Game.Modes.Osu.Objects; using osu.Game.Modes.Osu.Objects;
using osu.Game.Modes.Osu.UI; using osu.Game.Modes.Osu.UI;
@ -96,8 +95,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

@ -21,8 +21,10 @@ namespace osu.Game.Modes.Osu.UI
protected override IBeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter(); protected override IBeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
protected override IBeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
protected override Playfield<OsuHitObject, OsuJudgementInfo> CreatePlayfield() => new OsuPlayfield(); protected override Playfield<OsuHitObject, OsuJudgementInfo> CreatePlayfield() => new OsuPlayfield();
protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager(); protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager();
protected override DrawableHitObject<OsuHitObject, OsuJudgementInfo> GetVisualRepresentation(OsuHitObject h) protected override DrawableHitObject<OsuHitObject, OsuJudgementInfo> GetVisualRepresentation(OsuHitObject h)

View File

@ -44,8 +44,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\OsuBeatmapConverter.cs" /> <Compile Include="Beatmaps\OsuBeatmapConverter.cs" />
<Compile Include="Objects\BezierApproximator.cs" /> <Compile Include="Beatmaps\OsuBeatmapProcessor.cs" />
<Compile Include="Objects\CircularArcApproximator.cs" />
<Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" /> <Compile Include="Objects\Drawables\DrawableOsuHitObject.cs" />
<Compile Include="Objects\Drawables\Connections\ConnectionRenderer.cs" /> <Compile Include="Objects\Drawables\Connections\ConnectionRenderer.cs" />
<Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" /> <Compile Include="Objects\Drawables\Connections\FollowPointRenderer.cs" />
@ -68,9 +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\SliderTick.cs" /> <Compile Include="Objects\SliderTick.cs" />
<Compile Include="OsuAutoReplay.cs" /> <Compile Include="OsuAutoReplay.cs" />
<Compile Include="OsuDifficultyCalculator.cs" /> <Compile Include="OsuDifficultyCalculator.cs" />

View File

@ -0,0 +1,19 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Modes.Taiko.Objects;
namespace osu.Game.Modes.Taiko.Beatmaps
{
internal class TaikoBeatmapProcessor : IBeatmapProcessor<TaikoBaseHit>
{
public void SetDefaults(TaikoBaseHit hitObject, Beatmap<TaikoBaseHit> beatmap)
{
}
public void PostProcess(Beatmap<TaikoBaseHit> beatmap)
{
}
}
}

View File

@ -29,8 +29,10 @@ namespace osu.Game.Modes.Taiko.Objects.Drawable
{ {
Texture = textures.Get(@"Menu/logo"); Texture = textures.Get(@"Menu/logo");
double duration = 0;
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

@ -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.Mods; using osu.Game.Modes.Mods;
using osu.Game.Modes.Objects;
using osu.Game.Modes.Taiko.Mods; using osu.Game.Modes.Taiko.Mods;
using osu.Game.Modes.Taiko.UI; using osu.Game.Modes.Taiko.UI;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
@ -91,8 +90,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

@ -19,8 +19,10 @@ namespace osu.Game.Modes.Taiko.UI
protected override IBeatmapConverter<TaikoBaseHit> CreateBeatmapConverter() => new TaikoBeatmapConverter(); protected override IBeatmapConverter<TaikoBaseHit> CreateBeatmapConverter() => new TaikoBeatmapConverter();
protected override IBeatmapProcessor<TaikoBaseHit> CreateBeatmapProcessor() => new TaikoBeatmapProcessor();
protected override Playfield<TaikoBaseHit, TaikoJudgementInfo> CreatePlayfield() => new TaikoPlayfield(); protected override Playfield<TaikoBaseHit, TaikoJudgementInfo> CreatePlayfield() => new TaikoPlayfield();
protected override DrawableHitObject<TaikoBaseHit, TaikoJudgementInfo> GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h); protected override DrawableHitObject<TaikoBaseHit, TaikoJudgementInfo> GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h);
} }
} }

View File

@ -48,6 +48,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Beatmaps\TaikoBeatmapConverter.cs" /> <Compile Include="Beatmaps\TaikoBeatmapConverter.cs" />
<Compile Include="Beatmaps\TaikoBeatmapProcessor.cs" />
<Compile Include="Judgements\TaikoJudgementInfo.cs" /> <Compile Include="Judgements\TaikoJudgementInfo.cs" />
<Compile Include="TaikoDifficultyCalculator.cs" /> <Compile Include="TaikoDifficultyCalculator.cs" />
<Compile Include="Objects\Drawable\DrawableTaikoHit.cs" /> <Compile Include="Objects\Drawable\DrawableTaikoHit.cs" />

View File

@ -8,9 +8,9 @@ using OpenTK.Graphics;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
using osu.Game.Modes; using osu.Game.Modes;
using osu.Game.Modes.Osu;
using osu.Game.Modes.Osu.Objects;
using osu.Game.Tests.Resources; using osu.Game.Tests.Resources;
using osu.Game.Modes.Osu;
using osu.Game.Modes.Objects.Legacy;
namespace osu.Game.Tests.Beatmaps.Formats namespace osu.Game.Tests.Beatmaps.Formats
{ {
@ -133,16 +133,16 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu")) using (var stream = Resource.OpenResource("Soleily - Renatus (Gamu) [Insane].osu"))
{ {
var beatmap = decoder.Decode(new StreamReader(stream)); var beatmap = decoder.Decode(new StreamReader(stream));
var slider = beatmap.HitObjects[0] as Slider; var slider = beatmap.HitObjects[0] as LegacySlider;
Assert.IsNotNull(slider); Assert.IsNotNull(slider);
Assert.AreEqual(new Vector2(192, 168), slider.Position); Assert.AreEqual(new Vector2(192, 168), slider.Position);
Assert.AreEqual(956, slider.StartTime); Assert.AreEqual(956, slider.StartTime);
Assert.AreEqual(SampleType.None, slider.Sample.Type); Assert.AreEqual(SampleType.None, slider.Sample.Type);
var circle = beatmap.HitObjects[1] as HitCircle; var hit = beatmap.HitObjects[1] as LegacyHit;
Assert.IsNotNull(circle); Assert.IsNotNull(hit);
Assert.AreEqual(new Vector2(304, 56), circle.Position); Assert.AreEqual(new Vector2(304, 56), hit.Position);
Assert.AreEqual(1285, circle.StartTime); Assert.AreEqual(1285, hit.StartTime);
Assert.AreEqual(SampleType.Clap, circle.Sample.Type); Assert.AreEqual(SampleType.Clap, hit.Sample.Type);
} }
} }
} }

View File

@ -19,7 +19,13 @@ namespace osu.Game.Beatmaps
{ {
public BeatmapInfo BeatmapInfo; public BeatmapInfo BeatmapInfo;
public List<ControlPoint> ControlPoints; public List<ControlPoint> ControlPoints;
public List<Color4> ComboColors; public readonly List<Color4> ComboColors = new List<Color4>
{
new Color4(17, 136, 170, 255),
new Color4(102, 136, 0, 255),
new Color4(204, 102, 0, 255),
new Color4(121, 9, 13, 255)
};
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
@ -34,9 +40,9 @@ namespace osu.Game.Beatmaps
/// <param name="original">The original beatmap to use the parameters of.</param> /// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original = null) public Beatmap(Beatmap original = null)
{ {
BeatmapInfo = original?.BeatmapInfo; BeatmapInfo = original?.BeatmapInfo ?? BeatmapInfo;
ControlPoints = original?.ControlPoints; ControlPoints = original?.ControlPoints ?? ControlPoints;
ComboColors = original?.ComboColors; ComboColors = original?.ComboColors ?? ComboColors;
} }
public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength; public double BPMMaximum => 60000 / (ControlPoints?.Where(c => c.BeatLength != 0).OrderBy(c => c.BeatLength).FirstOrDefault() ?? ControlPoint.Default).BeatLength;

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using OpenTK.Graphics;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using osu.Game.Database; using osu.Game.Database;
@ -31,9 +30,7 @@ namespace osu.Game.Beatmaps.Formats
public virtual Beatmap Decode(TextReader stream) public virtual Beatmap Decode(TextReader stream)
{ {
Beatmap b = ParseFile(stream); return ParseFile(stream);
Process(b);
return b;
} }
public virtual void Decode(TextReader stream, Beatmap beatmap) public virtual void Decode(TextReader stream, Beatmap beatmap)
@ -41,20 +38,12 @@ namespace osu.Game.Beatmaps.Formats
ParseFile(stream, beatmap); ParseFile(stream, beatmap);
} }
public virtual Beatmap Process(Beatmap beatmap)
{
ApplyColours(beatmap);
return beatmap;
}
protected virtual Beatmap ParseFile(TextReader stream) protected virtual Beatmap ParseFile(TextReader stream)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {
HitObjects = new List<HitObject>(), HitObjects = new List<HitObject>(),
ControlPoints = new List<ControlPoint>(), ControlPoints = new List<ControlPoint>(),
ComboColors = new List<Color4>(),
BeatmapInfo = new BeatmapInfo BeatmapInfo = new BeatmapInfo
{ {
Metadata = new BeatmapMetadata(), Metadata = new BeatmapMetadata(),
@ -65,25 +54,5 @@ namespace osu.Game.Beatmaps.Formats
return beatmap; return beatmap;
} }
protected abstract void ParseFile(TextReader stream, Beatmap beatmap); protected abstract void ParseFile(TextReader stream, Beatmap beatmap);
public virtual void ApplyColours(Beatmap b)
{
List<Color4> colours = b.ComboColors ?? new List<Color4> {
new Color4(17, 136, 170, 255),
new Color4(102, 136, 0, 255),
new Color4(204, 102, 0, 255),
new Color4(121, 9, 13, 255),
};
if (colours.Count == 0) return;
int i = -1;
foreach (HitObject h in b.HitObjects)
{
if (h.NewCombo || i == -1) i = (i + 1) % colours.Count;
h.Colour = colours[i];
}
}
} }
} }

View File

@ -212,14 +212,23 @@ namespace osu.Game.Beatmaps.Formats
beatmap.ControlPoints.Add(cp); beatmap.ControlPoints.Add(cp);
} }
private void handleColours(Beatmap beatmap, string key, string val) private void handleColours(Beatmap beatmap, string key, string val, ref bool hasCustomColours)
{ {
string[] split = val.Split(','); string[] split = val.Split(',');
if (split.Length != 3) if (split.Length != 3)
throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {val}"); throw new InvalidOperationException($@"Color specified in incorrect format (should be R,G,B): {val}");
byte r, g, b; byte r, g, b;
if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b)) if (!byte.TryParse(split[0], out r) || !byte.TryParse(split[1], out g) || !byte.TryParse(split[2], out b))
throw new InvalidOperationException(@"Color must be specified with 8-bit integer components"); throw new InvalidOperationException(@"Color must be specified with 8-bit integer components");
if (!hasCustomColours)
{
beatmap.ComboColors.Clear();
hasCustomColours = true;
}
// Note: the combo index specified in the beatmap is discarded // Note: the combo index specified in the beatmap is discarded
if (key.StartsWith(@"Combo")) if (key.StartsWith(@"Combo"))
{ {
@ -237,6 +246,8 @@ namespace osu.Game.Beatmaps.Formats
{ {
HitObjectParser parser = null; HitObjectParser parser = null;
bool hasCustomColours = false;
var section = Section.None; var section = Section.None;
while (true) while (true)
{ {
@ -265,7 +276,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);
@ -283,16 +294,14 @@ namespace osu.Game.Beatmaps.Formats
handleTimingPoints(beatmap, val); handleTimingPoints(beatmap, val);
break; break;
case Section.Colours: case Section.Colours:
handleColours(beatmap, key, val); handleColours(beatmap, key, val, ref hasCustomColours);
break; break;
case Section.HitObjects: case Section.HitObjects:
var obj = parser?.Parse(val); var obj = parser?.Parse(val);
if (obj != null) if (obj != null)
{
obj.SetDefaultsFromBeatmap(beatmap);
beatmap.HitObjects.Add(obj); beatmap.HitObjects.Add(obj);
}
break; break;
} }
} }

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

View File

@ -0,0 +1,31 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects;
namespace osu.Game.Beatmaps
{
/// <summary>
/// Processes a post-converted Beatmap.
/// </summary>
/// <typeparam name="T">The type of HitObject contained in the Beatmap.</typeparam>
public interface IBeatmapProcessor<T>
where T : HitObject
{
/// <summary>
/// Sets default values for a HitObject.
/// </summary>
/// <param name="hitObject">The HitObject to set default values for.</param>
/// <param name="beatmap">The Beatmap to extract the default values from.</param>
void SetDefaults(T hitObject, Beatmap<T> beatmap);
/// <summary>
/// Post-processes a Beatmap to add mode-specific components that aren't added during conversion.
/// <para>
/// An example of such a usage is for combo colours.
/// </para>
/// </summary>
/// <param name="beatmap">The Beatmap to process.</param>
void PostProcess(Beatmap<T> beatmap);
}
}

View File

@ -4,7 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using OpenTK; using OpenTK;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Objects
{ {
public class BezierApproximator public class BezierApproximator
{ {

View File

@ -1,12 +1,12 @@
// 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 OpenTK;
using osu.Framework.MathUtils;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.MathUtils;
using OpenTK;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Objects
{ {
public class CircularArcApproximator public class CircularArcApproximator
{ {

View File

@ -0,0 +1,49 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Game.Modes.Objects.Types;
using System.Collections.Generic;
namespace osu.Game.Modes.Objects
{
public class CurvedHitObject : HitObject, IHasCurve
{
public SliderCurve Curve { get; } = new SliderCurve();
public int RepeatCount { get; set; } = 1;
public double EndTime => 0;
public double Duration => 0;
public List<Vector2> ControlPoints
{
get { return Curve.ControlPoints; }
set { Curve.ControlPoints = value; }
}
public CurveType CurveType
{
get { return Curve.CurveType; }
set { Curve.CurveType = value; }
}
public double Distance
{
get { return Curve.Distance; }
set { Curve.Distance = value; }
}
public Vector2 PositionAt(double progress) => Curve.PositionAt(ProgressAt(progress));
public double ProgressAt(double progress)
{
var p = progress * RepeatCount % 1;
if (RepeatAt(progress) % 2 == 1)
p = 1 - p;
return p;
}
public int RepeatAt(double progress) => (int)(progress * RepeatCount);
}
}

View File

@ -10,6 +10,7 @@ using osu.Framework.Audio.Sample;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Judgements; using osu.Game.Modes.Judgements;
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
{ {
@ -89,7 +90,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);

View File

@ -1,30 +1,26 @@
// 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.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;
public virtual void SetDefaultsFromBeatmap(Beatmap beatmap) { }
} }
} }

View File

@ -0,0 +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;
using OpenTK;
namespace osu.Game.Modes.Objects.Legacy
{
/// <summary>
/// Legacy Hit-type, used for parsing Beatmaps.
/// </summary>
public sealed class LegacyHit : HitObject, IHasPosition, IHasCombo
{
public Vector2 Position { get; set; }
public bool NewCombo { get; set; }
}
}

View File

@ -0,0 +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 System;
namespace osu.Game.Modes.Objects.Legacy
{
[Flags]
public enum LegacyHitObjectType
{
Circle = 1 << 0,
Slider = 1 << 1,
NewCombo = 1 << 2,
Spinner = 1 << 3,
ColourHax = 112,
Hold = 1 << 7
}
}

View File

@ -0,0 +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 OpenTK;
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Objects.Legacy
{
/// <summary>
/// Legacy Hold-type, used for parsing "specials" in beatmaps.
/// </summary>
public sealed class LegacyHold : HitObject, IHasPosition, IHasCombo, IHasHold
{
public Vector2 Position { get; set; }
public bool NewCombo { get; set; }
}
}

View File

@ -0,0 +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;
using OpenTK;
namespace osu.Game.Modes.Objects.Legacy
{
/// <summary>
/// Legacy Slider-type, used for parsing Beatmaps.
/// </summary>
public sealed class LegacySlider : CurvedHitObject, IHasPosition, IHasCombo
{
public Vector2 Position { get; set; }
public bool NewCombo { get; set; }
}
}

View File

@ -0,0 +1,17 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Modes.Objects.Types;
namespace osu.Game.Modes.Objects.Legacy
{
/// <summary>
/// Legacy Spinner-type, used for parsing Beatmaps.
/// </summary>
internal class LegacySpinner : HitObject, IHasEndTime
{
public double EndTime { get; set; }
public double Duration => EndTime - StartTime;
}
}

View File

@ -0,0 +1,119 @@
// 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.Modes.Objects.Legacy;
namespace osu.Game.Modes.Objects
{
internal class LegacyHitObjectParser : HitObjectParser
{
public override HitObject Parse(string text)
{
string[] split = text.Split(',');
var type = (LegacyHitObjectType)int.Parse(split[3]) & ~LegacyHitObjectType.ColourHax;
bool combo = type.HasFlag(LegacyHitObjectType.NewCombo);
type &= ~LegacyHitObjectType.NewCombo;
HitObject result;
if ((type & LegacyHitObjectType.Circle) > 0)
{
result = new LegacyHit
{
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
NewCombo = combo
};
}
else if ((type & LegacyHitObjectType.Slider) > 0)
{
CurveType curveType = CurveType.Catmull;
double length = 0;
List<Vector2> points = new List<Vector2> { new Vector2(int.Parse(split[0]), int.Parse(split[1])) };
string[] pointsplit = split[5].Split('|');
foreach (string t in pointsplit)
{
if (t.Length == 1)
{
switch (t)
{
case @"C":
curveType = CurveType.Catmull;
break;
case @"B":
curveType = CurveType.Bezier;
break;
case @"L":
curveType = CurveType.Linear;
break;
case @"P":
curveType = CurveType.PerfectCurve;
break;
}
continue;
}
string[] temp = t.Split(':');
Vector2 v = new Vector2(
(int)Convert.ToDouble(temp[0], CultureInfo.InvariantCulture),
(int)Convert.ToDouble(temp[1], CultureInfo.InvariantCulture)
);
points.Add(v);
}
int repeatCount = Convert.ToInt32(split[6], CultureInfo.InvariantCulture);
if (repeatCount > 9000)
throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
result = new LegacySlider
{
ControlPoints = points,
Distance = length,
CurveType = curveType,
RepeatCount = repeatCount,
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
NewCombo = combo
};
}
else if ((type & LegacyHitObjectType.Spinner) > 0)
{
result = new LegacySpinner
{
EndTime = Convert.ToDouble(split[5], CultureInfo.InvariantCulture)
};
}
else if ((type & LegacyHitObjectType.Hold) > 0)
{
// Note: Hold is generated by BMS converts
result = new LegacyHold
{
Position = new Vector2(int.Parse(split[0]), int.Parse(split[1])),
NewCombo = combo
};
}
else
throw new InvalidOperationException($@"Unknown hit object type {type}");
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture);
result.Sample = new HitSampleInfo
{
Type = (SampleType)int.Parse(split[4]),
Set = SampleSet.Soft,
};
// 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

@ -2,19 +2,20 @@
// 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.Collections.Generic; using System.Collections.Generic;
using OpenTK;
using System.Linq; using System.Linq;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Modes.Objects.Types;
using OpenTK;
namespace osu.Game.Modes.Osu.Objects namespace osu.Game.Modes.Objects
{ {
public class SliderCurve public class SliderCurve
{ {
public double Length; public double Distance;
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;
@ -82,12 +83,12 @@ namespace osu.Game.Modes.Osu.Objects
// Shorten slider curves that are too long compared to what's // Shorten slider curves that are too long compared to what's
// in the .osu file. // in the .osu file.
if (Length - l < d) if (Distance - l < d)
{ {
calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Length - l) / d); calculatedPath[i + 1] = calculatedPath[i] + diff * (float)((Distance - l) / d);
calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i); calculatedPath.RemoveRange(i + 2, calculatedPath.Count - 2 - i);
l = Length; l = Distance;
cumulativeLength.Add(l); cumulativeLength.Add(l);
break; break;
} }
@ -129,7 +130,7 @@ namespace osu.Game.Modes.Osu.Objects
private double progressToDistance(double progress) private double progressToDistance(double progress)
{ {
return MathHelper.Clamp(progress, 0, 1) * Length; return MathHelper.Clamp(progress, 0, 1) * Distance;
} }
private Vector2 interpolateVertices(int i, double d) private Vector2 interpolateVertices(int i, double d)

View File

@ -0,0 +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
namespace osu.Game.Modes.Objects.Types
{
public enum CurveType
{
Catmull,
Bezier,
Linear,
PerfectCurve
}
}

View File

@ -0,0 +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
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that is part of a combo.
/// </summary>
public interface IHasCombo
{
/// <summary>
/// Whether the HitObject starts a new combo.
/// </summary>
bool NewCombo { get; }
}
}

View File

@ -0,0 +1,52 @@
// 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 OpenTK;
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that has a curve.
/// </summary>
public interface IHasCurve : IHasDistance, IHasRepeats
{
/// <summary>
/// The curve.
/// </summary>
SliderCurve Curve { get; }
/// <summary>
/// The control points that shape the curve.
/// </summary>
List<Vector2> ControlPoints { get; }
/// <summary>
/// The type of curve.
/// </summary>
CurveType CurveType { get; }
/// <summary>
/// Computes the position on the curve at a given progress, accounting for repeat logic.
/// <para>
/// Ranges from [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.
/// </para>
/// </summary>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
Vector2 PositionAt(double progress);
/// <summary>
/// Finds the progress along the curve, accounting for repeat logic.
/// </summary>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
double ProgressAt(double progress);
/// <summary>
/// Determines which repeat of the curve the progress point is on.
/// </summary>
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
/// <returns>[0, RepeatCount] where 0 is the first run.</returns>
int RepeatAt(double progress);
}
}

View File

@ -0,0 +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
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that has a positional length.
/// </summary>
public interface IHasDistance : IHasEndTime
{
/// <summary>
/// The positional length of the HitObject.
/// </summary>
double Distance { get; }
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
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,12 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A special type of HitObject, mostly used for legacy conversion of "holds".
/// </summary>
public interface IHasHold
{
}
}

View File

@ -0,0 +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 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,16 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Modes.Objects.Types
{
/// <summary>
/// A HitObject that spans some length.
/// </summary>
public interface IHasRepeats : IHasEndTime
{
/// <summary>
/// The amount of times the HitObject repeats.
/// </summary>
int RepeatCount { get; }
}
}

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.Mods; using osu.Game.Modes.Mods;
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;
@ -34,8 +33,6 @@ namespace osu.Game.Modes
public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap beatmap); public abstract HitRenderer CreateHitRendererWith(WorkingBeatmap 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

@ -11,6 +11,7 @@ using osu.Game.Modes.Objects.Drawables;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Game.Modes.Judgements; using osu.Game.Modes.Judgements;
@ -86,7 +87,12 @@ namespace osu.Game.Modes.UI
protected HitRenderer(WorkingBeatmap beatmap) protected HitRenderer(WorkingBeatmap beatmap)
{ {
Debug.Assert(beatmap != null, "HitRenderer initialized with a null beatmap.");
// Convert + process the beatmap
Beatmap = CreateBeatmapConverter().Convert(beatmap.Beatmap); Beatmap = CreateBeatmapConverter().Convert(beatmap.Beatmap);
Beatmap.HitObjects.ForEach(h => CreateBeatmapProcessor().SetDefaults(h, Beatmap));
CreateBeatmapProcessor().PostProcess(Beatmap);
applyMods(beatmap.Mods.Value); applyMods(beatmap.Mods.Value);

View File

@ -21,6 +21,8 @@ using osu.Game.Database;
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
{ {
@ -90,11 +92,14 @@ namespace osu.Game.Screens.Select
if (beatmap.Beatmap != null) if (beatmap.Beatmap != null)
{ {
HitObject lastObject = beatmap.Beatmap.HitObjects.LastOrDefault();
double endTime = (lastObject as IHasEndTime)?.EndTime ?? lastObject?.StartTime ?? 0;
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

@ -74,6 +74,7 @@
<Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" /> <Compile Include="Beatmaps\Drawables\BeatmapBackgroundSprite.cs" />
<Compile Include="Beatmaps\DifficultyCalculator.cs" /> <Compile Include="Beatmaps\DifficultyCalculator.cs" />
<Compile Include="Beatmaps\IBeatmapCoverter.cs" /> <Compile Include="Beatmaps\IBeatmapCoverter.cs" />
<Compile Include="Beatmaps\IBeatmapProcessor.cs" />
<Compile Include="Database\ScoreDatabase.cs" /> <Compile Include="Database\ScoreDatabase.cs" />
<Compile Include="Graphics\Backgrounds\Triangles.cs" /> <Compile Include="Graphics\Backgrounds\Triangles.cs" />
<Compile Include="Graphics\Cursor\CursorTrail.cs" /> <Compile Include="Graphics\Cursor\CursorTrail.cs" />
@ -95,10 +96,27 @@
<Compile Include="Modes\Mods\ModType.cs" /> <Compile Include="Modes\Mods\ModType.cs" />
<Compile Include="Modes\Objects\Drawables\ArmedState.cs" /> <Compile Include="Modes\Objects\Drawables\ArmedState.cs" />
<Compile Include="Modes\Objects\Drawables\HitResult.cs" /> <Compile Include="Modes\Objects\Drawables\HitResult.cs" />
<Compile Include="Modes\Objects\BezierApproximator.cs" />
<Compile Include="Modes\Objects\CircularArcApproximator.cs" />
<Compile Include="Modes\Objects\CurvedHitObject.cs" />
<Compile Include="Modes\Objects\Legacy\LegacyHit.cs" />
<Compile Include="Modes\Objects\LegacyHitObjectParser.cs" />
<Compile Include="Modes\Objects\Legacy\LegacyHold.cs" />
<Compile Include="Modes\Objects\Legacy\LegacySlider.cs" />
<Compile Include="Modes\Objects\Legacy\LegacySpinner.cs" />
<Compile Include="Modes\Objects\SliderCurve.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\Judgements\JudgementInfo.cs" /> <Compile Include="Modes\Judgements\JudgementInfo.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\IHasHold.cs" />
<Compile Include="Modes\Objects\Legacy\LegacyHitObjectType.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" />