1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-06 06:53:21 +08:00

Merge remote-tracking branch 'upstream/master' into tab-control

This commit is contained in:
Drew DeVault 2017-03-15 20:15:28 -04:00
commit cac6c42aac
96 changed files with 1190 additions and 565 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,19 +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 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.Judgements;
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
{ {
@ -60,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,
ControlPoints = new List<Vector2> CurveObject = new CurvedHitObject
{ {
new Vector2(-200, 0), ControlPoints = new List<Vector2>
new Vector2(400, 0), {
new Vector2(-200, 0),
new Vector2(400, 0),
},
Distance = 400
}, },
Length = 400,
Position = new Vector2(-200, 0), Position = new Vector2(-200, 0),
Velocity = 1, Velocity = 1,
TickDistance = 100, TickDistance = 100,
@ -75,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;
@ -123,7 +128,7 @@ namespace osu.Desktop.VisualTests.Tests
private int depth; private int depth;
private void add(DrawableHitObject h) private void add(DrawableOsuHitObject h)
{ {
h.Anchor = Anchor.Centre; h.Anchor = Anchor.Centre;
h.Depth = depth++; h.Depth = depth++;

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

@ -23,7 +23,7 @@
<SignAssembly>false</SignAssembly> <SignAssembly>false</SignAssembly>
<TargetZone>LocalIntranet</TargetZone> <TargetZone>LocalIntranet</TargetZone>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects> <AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<PublishUrl>publish\</PublishUrl> <PublishUrl>publish\</PublishUrl>
<Install>true</Install> <Install>true</Install>
<InstallFrom>Disk</InstallFrom> <InstallFrom>Disk</InstallFrom>
@ -102,10 +102,6 @@
<HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath> <HintPath>$(SolutionDir)\packages\DeltaCompressionDotNet.1.1.0\lib\net20\DeltaCompressionDotNet.PatchApi.dll</HintPath>
<Private>True</Private> <Private>True</Private>
</Reference> </Reference>
<Reference Include="ICSharpCode.SharpZipLib, Version=0.86.0.518, Culture=neutral, PublicKeyToken=1b03e6acf1164f73, processorArchitecture=MSIL">
<HintPath>..\packages\SharpZipLib.0.86.0\lib\20\ICSharpCode.SharpZipLib.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL"> <Reference Include="Mono.Cecil, Version=0.9.6.0, Culture=neutral, PublicKeyToken=0738eb9f132ed756, processorArchitecture=MSIL">
<HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath> <HintPath>$(SolutionDir)\packages\Mono.Cecil.0.9.6.4\lib\net45\Mono.Cecil.dll</HintPath>
<Private>True</Private> <Private>True</Private>

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
@ -8,7 +7,6 @@ Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/maste
<package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" /> <package id="DeltaCompressionDotNet" version="1.1.0" targetFramework="net45" />
<package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" /> <package id="Microsoft.Net.Http" version="2.2.29" targetFramework="net45" />
<package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" /> <package id="Mono.Cecil" version="0.9.6.4" targetFramework="net45" />
<package id="SharpZipLib" version="0.86.0" targetFramework="net45" />
<package id="Splat" version="2.0.0" targetFramework="net45" /> <package id="Splat" version="2.0.0" targetFramework="net45" />
<package id="squirrel.windows" version="1.5.2" targetFramework="net45" /> <package id="squirrel.windows" version="1.5.2" targetFramework="net45" />
</packages> </packages>

View File

@ -1,7 +1,6 @@
// 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;
using osu.Game.Modes.Catch.Objects; using osu.Game.Modes.Catch.Objects;
using System.Collections.Generic; using System.Collections.Generic;

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

@ -0,0 +1,11 @@
// 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.Judgements;
namespace osu.Game.Modes.Catch.Judgements
{
public class CatchJudgementInfo : JudgementInfo
{
}
}

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

@ -3,13 +3,14 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Catch.Beatmaps; using osu.Game.Modes.Catch.Beatmaps;
using osu.Game.Modes.Catch.Judgements;
using osu.Game.Modes.Catch.Objects; using osu.Game.Modes.Catch.Objects;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Catch.UI namespace osu.Game.Modes.Catch.UI
{ {
public class CatchHitRenderer : HitRenderer<CatchBaseHit> public class CatchHitRenderer : HitRenderer<CatchBaseHit, CatchJudgementInfo>
{ {
public CatchHitRenderer(WorkingBeatmap beatmap) public CatchHitRenderer(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -18,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 Playfield<CatchBaseHit> CreatePlayfield() => new CatchPlayfield(); protected override IBeatmapProcessor<CatchBaseHit> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override Playfield<CatchBaseHit, CatchJudgementInfo> CreatePlayfield() => new CatchPlayfield();
protected override DrawableHitObject<CatchBaseHit> GetVisualRepresentation(CatchBaseHit h) => null;// new DrawableFruit(h);
protected override DrawableHitObject<CatchBaseHit, CatchJudgementInfo> GetVisualRepresentation(CatchBaseHit h) => null;
} }
} }

View File

@ -6,10 +6,11 @@ using osu.Framework.Graphics.Sprites;
using osu.Game.Modes.Catch.Objects; using osu.Game.Modes.Catch.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
using osu.Game.Modes.Catch.Judgements;
namespace osu.Game.Modes.Catch.UI namespace osu.Game.Modes.Catch.UI
{ {
public class CatchPlayfield : Playfield<CatchBaseHit> public class CatchPlayfield : Playfield<CatchBaseHit, CatchJudgementInfo>
{ {
public CatchPlayfield() public CatchPlayfield()
{ {

View File

@ -48,7 +48,9 @@
</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="Objects\CatchBaseHit.cs" /> <Compile Include="Objects\CatchBaseHit.cs" />
<Compile Include="Objects\Drawable\DrawableFruit.cs" /> <Compile Include="Objects\Drawable\DrawableFruit.cs" />
<Compile Include="Objects\Droplet.cs" /> <Compile Include="Objects\Droplet.cs" />

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.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

@ -0,0 +1,11 @@
// 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.Judgements;
namespace osu.Game.Modes.Mania.Judgements
{
public class ManiaJudgementInfo : JudgementInfo
{
}
}

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

@ -3,13 +3,14 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Mania.Beatmaps; using osu.Game.Modes.Mania.Beatmaps;
using osu.Game.Modes.Mania.Judgements;
using osu.Game.Modes.Mania.Objects; using osu.Game.Modes.Mania.Objects;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Mania.UI namespace osu.Game.Modes.Mania.UI
{ {
public class ManiaHitRenderer : HitRenderer<ManiaBaseHit> public class ManiaHitRenderer : HitRenderer<ManiaBaseHit, ManiaJudgementInfo>
{ {
private readonly int columns; private readonly int columns;
@ -21,16 +22,10 @@ namespace osu.Game.Modes.Mania.UI
protected override IBeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter(); protected override IBeatmapConverter<ManiaBaseHit> CreateBeatmapConverter() => new ManiaBeatmapConverter();
protected override Playfield<ManiaBaseHit> CreatePlayfield() => new ManiaPlayfield(columns); protected override IBeatmapProcessor<ManiaBaseHit> CreateBeatmapProcessor() => new ManiaBeatmapProcessor();
protected override Playfield<ManiaBaseHit, ManiaJudgementInfo> CreatePlayfield() => new ManiaPlayfield(columns);
protected override DrawableHitObject<ManiaBaseHit> GetVisualRepresentation(ManiaBaseHit h)
{ protected override DrawableHitObject<ManiaBaseHit, ManiaJudgementInfo> GetVisualRepresentation(ManiaBaseHit h) => null;
return null;
//return new DrawableNote(h)
//{
// Position = new Vector2((float)(h.Column + 0.5) / columns, -0.1f),
// RelativePositionAxes = Axes.Both
//};
}
} }
} }

View File

@ -7,10 +7,11 @@ using osu.Game.Modes.Mania.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Modes.Mania.Judgements;
namespace osu.Game.Modes.Mania.UI namespace osu.Game.Modes.Mania.UI
{ {
public class ManiaPlayfield : Playfield<ManiaBaseHit> public class ManiaPlayfield : Playfield<ManiaBaseHit, ManiaJudgementInfo>
{ {
public ManiaPlayfield(int columns) public ManiaPlayfield(int columns)
{ {

View File

@ -48,6 +48,8 @@
</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="ManiaDifficultyCalculator.cs" /> <Compile Include="ManiaDifficultyCalculator.cs" />
<Compile Include="Objects\Drawable\DrawableNote.cs" /> <Compile Include="Objects\Drawable\DrawableNote.cs" />
<Compile Include="Objects\HoldNote.cs" /> <Compile Include="Objects\HoldNote.cs" />

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

@ -1,13 +1,14 @@
// 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 OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Objects; 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
{ {
@ -17,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)
@ -62,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;
@ -116,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

@ -0,0 +1,50 @@
// 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.Judgements;
using osu.Game.Modes.Osu.Objects.Drawables;
namespace osu.Game.Modes.Osu.Judgements
{
public class OsuJudgementInfo : JudgementInfo
{
/// <summary>
/// The positional hit offset.
/// </summary>
public Vector2 PositionOffset;
/// <summary>
/// The score the user achieved.
/// </summary>
public OsuScoreResult Score;
/// <summary>
/// The score which would be achievable on a perfect hit.
/// </summary>
public OsuScoreResult MaxScore = OsuScoreResult.Hit300;
public int ScoreValue => scoreToInt(Score);
public int MaxScoreValue => scoreToInt(MaxScore);
private int scoreToInt(OsuScoreResult result)
{
switch (result)
{
default:
return 0;
case OsuScoreResult.Hit50:
return 50;
case OsuScoreResult.Hit100:
return 100;
case OsuScoreResult.Hit300:
return 300;
case OsuScoreResult.SliderTick:
return 10;
}
}
public ComboResult Combo;
}
}

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,16 +36,16 @@ 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;
((PositionalJudgementInfo)Judgement).PositionOffset = Vector2.Zero; //todo: set to correct value Judgement.PositionOffset = Vector2.Zero; //todo: set to correct value
UpdateJudgement(true); UpdateJudgement(true);
return true; return true;
}, },
@ -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,
} }
}; };
@ -80,12 +81,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
double hitOffset = Math.Abs(Judgement.TimeOffset); double hitOffset = Math.Abs(Judgement.TimeOffset);
OsuJudgementInfo osuJudgement = (OsuJudgementInfo)Judgement;
if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50)) if (hitOffset < HitObject.HitWindowFor(OsuScoreResult.Hit50))
{ {
Judgement.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
osuJudgement.Score = HitObject.ScoreResultForOffset(hitOffset); Judgement.Score = HitObject.ScoreResultForOffset(hitOffset);
} }
else else
Judgement.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
@ -118,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

@ -3,10 +3,11 @@
using System.ComponentModel; using System.ComponentModel;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu.Objects.Drawables namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject> public class DrawableOsuHitObject : DrawableHitObject<OsuHitObject, OsuJudgementInfo>
{ {
public const float TIME_PREEMPT = 600; public const float TIME_PREEMPT = 600;
public const float TIME_FADEIN = 400; public const float TIME_FADEIN = 400;
@ -17,7 +18,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
} }
protected override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 }; protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.Hit300 };
protected override void UpdateState(ArmedState state) protected override void UpdateState(ArmedState state)
{ {
@ -45,42 +46,6 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
} }
} }
public class OsuJudgementInfo : PositionalJudgementInfo
{
/// <summary>
/// The score the user achieved.
/// </summary>
public OsuScoreResult Score;
/// <summary>
/// The score which would be achievable on a perfect hit.
/// </summary>
public OsuScoreResult MaxScore = OsuScoreResult.Hit300;
public int ScoreValue => scoreToInt(Score);
public int MaxScoreValue => scoreToInt(MaxScore);
private int scoreToInt(OsuScoreResult result)
{
switch (result)
{
default:
return 0;
case OsuScoreResult.Hit50:
return 50;
case OsuScoreResult.Hit100:
return 100;
case OsuScoreResult.Hit300:
return 300;
case OsuScoreResult.SliderTick:
return 10;
}
}
public ComboResult Combo;
}
public enum ComboResult public enum ComboResult
{ {
[Description(@"")] [Description(@"")]

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,27 +125,24 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
var j = (OsuJudgementInfo)Judgement; if (!userTriggered && Time.Current >= slider.EndTime)
var sc = (OsuJudgementInfo)initialCircle.Judgement;
if (!userTriggered && Time.Current >= HitObject.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);
if (sc.Result == HitResult.Hit) if (initialCircle.Judgement.Result == HitResult.Hit)
ticksHit++; ticksHit++;
var hitFraction = (double)ticksHit / ticksCount; var hitFraction = (double)ticksHit / ticksCount;
if (hitFraction == 1 && sc.Score == OsuScoreResult.Hit300) if (hitFraction == 1 && initialCircle.Judgement.Score == OsuScoreResult.Hit300)
j.Score = OsuScoreResult.Hit300; Judgement.Score = OsuScoreResult.Hit300;
else if (hitFraction >= 0.5 && sc.Score >= OsuScoreResult.Hit100) else if (hitFraction >= 0.5 && initialCircle.Judgement.Score >= OsuScoreResult.Hit100)
j.Score = OsuScoreResult.Hit100; Judgement.Score = OsuScoreResult.Hit100;
else if (hitFraction > 0) else if (hitFraction > 0)
j.Score = OsuScoreResult.Hit50; Judgement.Score = OsuScoreResult.Hit50;
else else
j.Score = OsuScoreResult.Miss; Judgement.Score = OsuScoreResult.Miss;
j.Result = j.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss; Judgement.Result = Judgement.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss;
} }
} }
@ -165,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

@ -10,6 +10,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -26,7 +27,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
public override bool RemoveWhenNotAlive => false; public override bool RemoveWhenNotAlive => false;
protected override JudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick }; protected override OsuJudgementInfo CreateJudgementInfo() => new OsuJudgementInfo { MaxScore = OsuScoreResult.SliderTick };
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{ {
@ -47,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,
} }
}; };
@ -71,12 +72,10 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
protected override void CheckJudgement(bool userTriggered) protected override void CheckJudgement(bool userTriggered)
{ {
var j = (OsuJudgementInfo)Judgement;
if (Judgement.TimeOffset >= 0) if (Judgement.TimeOffset >= 0)
{ {
j.Result = Tracking ? HitResult.Hit : HitResult.Miss; Judgement.Result = Tracking ? HitResult.Hit : HitResult.Miss;
j.Score = Tracking ? OsuScoreResult.SliderTick : OsuScoreResult.Miss; Judgement.Score = Tracking ? OsuScoreResult.SliderTick : OsuScoreResult.Miss;
} }
} }

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
{ {
@ -75,35 +75,33 @@ namespace osu.Game.Modes.Osu.Objects.Drawables
{ {
if (Time.Current < HitObject.StartTime) return; if (Time.Current < HitObject.StartTime) return;
var j = (OsuJudgementInfo)Judgement;
disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100); disc.ScaleTo(Interpolation.ValueAt(Math.Sqrt(Progress), scaleToCircle, Vector2.One, 0, 1), 100);
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)
{ {
j.Score = OsuScoreResult.Hit300; Judgement.Score = OsuScoreResult.Hit300;
j.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
} }
else if (Progress > .9) else if (Progress > .9)
{ {
j.Score = OsuScoreResult.Hit100; Judgement.Score = OsuScoreResult.Hit100;
j.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
} }
else if (Progress > .75) else if (Progress > .75)
{ {
j.Score = OsuScoreResult.Hit50; Judgement.Score = OsuScoreResult.Hit50;
j.Result = HitResult.Hit; Judgement.Result = HitResult.Hit;
} }
else else
{ {
j.Score = OsuScoreResult.Miss; Judgement.Score = OsuScoreResult.Miss;
if (Time.Current >= HitObject.EndTime) if (Time.Current >= spinner.EndTime)
j.Result = HitResult.Miss; Judgement.Result = HitResult.Miss;
} }
} }
} }
@ -140,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

@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Transforms; using osu.Framework.Graphics.Transforms;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Judgements;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;

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

@ -1,8 +1,9 @@
// 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.Judgements;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Objects.Drawables; using osu.Game.Modes.Osu.Judgements;
namespace osu.Game.Modes.Osu namespace osu.Game.Modes.Osu
{ {

View File

@ -4,6 +4,7 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Osu.Beatmaps; using osu.Game.Modes.Osu.Beatmaps;
using osu.Game.Modes.Osu.Judgements;
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 osu.Game.Modes.UI; using osu.Game.Modes.UI;
@ -11,7 +12,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Modes.Osu.UI namespace osu.Game.Modes.Osu.UI
{ {
public class OsuHitRenderer : HitRenderer<OsuHitObject> public class OsuHitRenderer : HitRenderer<OsuHitObject, OsuJudgementInfo>
{ {
public OsuHitRenderer(WorkingBeatmap beatmap) public OsuHitRenderer(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -20,11 +21,13 @@ namespace osu.Game.Modes.Osu.UI
protected override IBeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter(); protected override IBeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
protected override Playfield<OsuHitObject> CreatePlayfield() => new OsuPlayfield(); protected override IBeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
protected override Playfield<OsuHitObject, OsuJudgementInfo> CreatePlayfield() => new OsuPlayfield();
protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager(); protected override KeyConversionInputManager CreateKeyConversionInputManager() => new OsuKeyConversionInputManager();
protected override DrawableHitObject<OsuHitObject> GetVisualRepresentation(OsuHitObject h) protected override DrawableHitObject<OsuHitObject, OsuJudgementInfo> GetVisualRepresentation(OsuHitObject h)
{ {
var circle = h as HitCircle; var circle = h as HitCircle;
if (circle != null) if (circle != null)

View File

@ -11,11 +11,12 @@ using osu.Game.Modes.Osu.Objects.Drawables.Connections;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using System.Linq; using System.Linq;
using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Cursor;
using osu.Game.Modes.Osu.Judgements;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Modes.Osu.UI namespace osu.Game.Modes.Osu.UI
{ {
public class OsuPlayfield : Playfield<OsuHitObject> public class OsuPlayfield : Playfield<OsuHitObject, OsuJudgementInfo>
{ {
private Container approachCircles; private Container approachCircles;
private Container judgementLayer; private Container judgementLayer;
@ -65,16 +66,13 @@ namespace osu.Game.Modes.Osu.UI
AddInternal(new OsuCursorContainer { Colour = Color4.LightYellow }); AddInternal(new OsuCursorContainer { Colour = Color4.LightYellow });
} }
public override void Add(DrawableHitObject<OsuHitObject> h) public override void Add(DrawableHitObject<OsuHitObject, OsuJudgementInfo> h)
{ {
h.Depth = (float)h.HitObject.StartTime; h.Depth = (float)h.HitObject.StartTime;
IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach; IDrawableHitObjectWithProxiedApproach c = h as IDrawableHitObjectWithProxiedApproach;
if (c != null) if (c != null)
{
approachCircles.Add(c.ProxiedLayer.CreateProxy()); approachCircles.Add(c.ProxiedLayer.CreateProxy());
}
h.OnJudgement += judgement;
base.Add(h); base.Add(h);
} }
@ -86,9 +84,9 @@ namespace osu.Game.Modes.Osu.UI
.OrderBy(h => h.StartTime); .OrderBy(h => h.StartTime);
} }
private void judgement(DrawableHitObject<OsuHitObject> h, JudgementInfo j) public override void OnJudgement(DrawableHitObject<OsuHitObject, OsuJudgementInfo> judgedObject)
{ {
HitExplosion explosion = new HitExplosion((OsuJudgementInfo)j, h.HitObject); HitExplosion explosion = new HitExplosion(judgedObject.Judgement, judgedObject.HitObject);
judgementLayer.Add(explosion); judgementLayer.Add(explosion);
} }

View File

@ -44,11 +44,11 @@
</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" />
<Compile Include="Judgements\OsuJudgementInfo.cs" />
<Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" /> <Compile Include="Objects\Drawables\Pieces\ApproachCircle.cs" />
<Compile Include="Objects\Drawables\Pieces\SpinnerBackground.cs" /> <Compile Include="Objects\Drawables\Pieces\SpinnerBackground.cs" />
<Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" /> <Compile Include="Objects\Drawables\Pieces\CirclePiece.cs" />
@ -67,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

@ -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.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

@ -0,0 +1,11 @@
// 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.Judgements;
namespace osu.Game.Modes.Taiko.Judgements
{
public class TaikoJudgementInfo : JudgementInfo
{
}
}

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

@ -4,12 +4,13 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using osu.Game.Modes.Taiko.Beatmaps; using osu.Game.Modes.Taiko.Beatmaps;
using osu.Game.Modes.Taiko.Judgements;
using osu.Game.Modes.Taiko.Objects; using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
public class TaikoHitRenderer : HitRenderer<TaikoBaseHit> public class TaikoHitRenderer : HitRenderer<TaikoBaseHit, TaikoJudgementInfo>
{ {
public TaikoHitRenderer(WorkingBeatmap beatmap) public TaikoHitRenderer(WorkingBeatmap beatmap)
: base(beatmap) : base(beatmap)
@ -18,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 Playfield<TaikoBaseHit> CreatePlayfield() => new TaikoPlayfield(); protected override IBeatmapProcessor<TaikoBaseHit> CreateBeatmapProcessor() => new TaikoBeatmapProcessor();
protected override Playfield<TaikoBaseHit, TaikoJudgementInfo> CreatePlayfield() => new TaikoPlayfield();
protected override DrawableHitObject<TaikoBaseHit> GetVisualRepresentation(TaikoBaseHit h) => null;// new DrawableTaikoHit(h);
protected override DrawableHitObject<TaikoBaseHit, TaikoJudgementInfo> GetVisualRepresentation(TaikoBaseHit h) => null;
} }
} }

View File

@ -9,10 +9,11 @@ using osu.Game.Modes.Taiko.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Game.Modes.Taiko.Judgements;
namespace osu.Game.Modes.Taiko.UI namespace osu.Game.Modes.Taiko.UI
{ {
public class TaikoPlayfield : Playfield<TaikoBaseHit> public class TaikoPlayfield : Playfield<TaikoBaseHit, TaikoJudgementInfo>
{ {
public TaikoPlayfield() public TaikoPlayfield()
{ {

View File

@ -48,6 +48,8 @@
</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="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" />

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

@ -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

@ -0,0 +1,14 @@
// 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.Drawables;
namespace osu.Game.Modes.Judgements
{
public class JudgementInfo
{
public ulong? ComboAtHit;
public HitResult? Result;
public double TimeOffset;
}
}

View File

@ -1,7 +1,6 @@
// 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; using osu.Game.Modes.Objects;
using osu.Game.Modes.UI; using osu.Game.Modes.UI;

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

@ -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.Drawables
{
public enum ArmedState
{
Idle,
Hit,
Miss
}
}

View File

@ -3,26 +3,27 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using osu.Framework; using osu.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
using osu.Game.Beatmaps.Samples; using osu.Game.Beatmaps.Samples;
using OpenTK; 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
{ {
public abstract class DrawableHitObject : Container, IStateful<ArmedState> public abstract class DrawableHitObject<TJudgement> : Container, IStateful<ArmedState>
where TJudgement : JudgementInfo
{ {
public override bool HandleInput => Interactive; public override bool HandleInput => Interactive;
public bool Interactive = true; public bool Interactive = true;
public JudgementInfo Judgement; public TJudgement Judgement;
protected abstract JudgementInfo CreateJudgementInfo(); protected abstract TJudgement CreateJudgementInfo();
protected abstract void UpdateState(ArmedState state); protected abstract void UpdateState(ArmedState state);
@ -67,14 +68,15 @@ namespace osu.Game.Modes.Objects.Drawables
} }
} }
public abstract class DrawableHitObject<HitObjectType> : DrawableHitObject public abstract class DrawableHitObject<TObject, TJudgement> : DrawableHitObject<TJudgement>
where HitObjectType : HitObject where TObject : HitObject
where TJudgement : JudgementInfo
{ {
public event Action<DrawableHitObject<HitObjectType>, JudgementInfo> OnJudgement; public event Action<DrawableHitObject<TObject, TJudgement>> OnJudgement;
public HitObjectType HitObject; public TObject HitObject;
protected DrawableHitObject(HitObjectType hitObject) protected DrawableHitObject(TObject hitObject)
{ {
HitObject = hitObject; HitObject = hitObject;
} }
@ -88,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);
@ -105,7 +109,7 @@ namespace osu.Game.Modes.Objects.Drawables
break; break;
} }
OnJudgement?.Invoke(this, Judgement); OnJudgement?.Invoke(this);
return true; return true;
} }
@ -138,44 +142,17 @@ 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, TJudgement>> nestedHitObjects;
protected IEnumerable<DrawableHitObject<HitObjectType>> NestedHitObjects => nestedHitObjects; protected IEnumerable<DrawableHitObject<TObject, TJudgement>> NestedHitObjects => nestedHitObjects;
protected void AddNested(DrawableHitObject<HitObjectType> h) protected void AddNested(DrawableHitObject<TObject, TJudgement> h)
{ {
if (nestedHitObjects == null) if (nestedHitObjects == null)
nestedHitObjects = new List<DrawableHitObject<HitObjectType>>(); nestedHitObjects = new List<DrawableHitObject<TObject, TJudgement>>();
h.OnJudgement += (d, j) => { OnJudgement?.Invoke(d, j); } ; h.OnJudgement += d => OnJudgement?.Invoke(d);
nestedHitObjects.Add(h); nestedHitObjects.Add(h);
} }
} }
public enum ArmedState
{
Idle,
Hit,
Miss
}
public class PositionalJudgementInfo : JudgementInfo
{
public Vector2 PositionOffset;
}
public class JudgementInfo
{
public ulong? ComboAtHit;
public HitResult? Result;
public double TimeOffset;
}
public enum HitResult
{
[Description(@"Miss")]
Miss,
[Description(@"Hit")]
Hit,
}
} }

View File

@ -0,0 +1,15 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Modes.Objects.Drawables
{
public enum HitResult
{
[Description(@"Miss")]
Miss,
[Description(@"Hit")]
Hit,
}
}

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

@ -2,9 +2,9 @@
// 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.Framework.Configuration; using osu.Framework.Configuration;
using osu.Game.Modes.Objects.Drawables;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes namespace osu.Game.Modes
{ {

View File

@ -5,25 +5,51 @@ using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Modes.Judgements;
using osu.Game.Modes.Mods; using osu.Game.Modes.Mods;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables; 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;
namespace osu.Game.Modes.UI namespace osu.Game.Modes.UI
{ {
/// <summary>
/// Base HitRenderer. Doesn't hold objects.
/// <para>
/// Should not be derived - derive <see cref="HitRenderer{TObject, TJudgement}"/> instead.
/// </para>
/// </summary>
public abstract class HitRenderer : Container public abstract class HitRenderer : Container
{ {
/// <summary>
/// The event that's fired when a hit object is judged.
/// </summary>
public event Action<JudgementInfo> OnJudgement; public event Action<JudgementInfo> OnJudgement;
/// <summary>
/// The event that's fired when all hit objects have been judged.
/// </summary>
public event Action OnAllJudged; public event Action OnAllJudged;
/// <summary>
/// The input manager for this HitRenderer.
/// </summary>
internal readonly PlayerInputManager InputManager = new PlayerInputManager(); internal readonly PlayerInputManager InputManager = new PlayerInputManager();
/// <summary>
/// The key conversion input manager for this HitRenderer.
/// </summary>
protected readonly KeyConversionInputManager KeyConversionInputManager; protected readonly KeyConversionInputManager KeyConversionInputManager;
/// <summary>
/// Whether all the HitObjects have been judged.
/// </summary>
protected abstract bool AllObjectsJudged { get; }
protected HitRenderer() protected HitRenderer()
{ {
KeyConversionInputManager = CreateKeyConversionInputManager(); KeyConversionInputManager = CreateKeyConversionInputManager();
@ -31,10 +57,9 @@ namespace osu.Game.Modes.UI
} }
/// <summary> /// <summary>
/// Whether all the HitObjects have been judged. /// Triggers a judgement for further processing.
/// </summary> /// </summary>
protected abstract bool AllObjectsJudged { get; } /// <param name="j">The judgement to trigger.</param>
protected void TriggerOnJudgement(JudgementInfo j) protected void TriggerOnJudgement(JudgementInfo j)
{ {
OnJudgement?.Invoke(j); OnJudgement?.Invoke(j);
@ -43,29 +68,92 @@ namespace osu.Game.Modes.UI
OnAllJudged?.Invoke(); OnAllJudged?.Invoke();
} }
/// <summary>
/// Creates a key conversion input manager.
/// </summary>
/// <returns>The input manager.</returns>
protected virtual KeyConversionInputManager CreateKeyConversionInputManager() => new KeyConversionInputManager(); protected virtual KeyConversionInputManager CreateKeyConversionInputManager() => new KeyConversionInputManager();
} }
/// <summary>
/// HitRenderer that applies conversion to Beatmaps. Does not contain a Playfield
/// and does not load drawable hit objects.
/// <para>
/// Should not be derived - derive <see cref="HitRenderer{TObject, TJudgement}"/> instead.
/// </para>
/// </summary>
/// <typeparam name="TObject">The type of HitObject contained by this HitRenderer.</typeparam>
public abstract class HitRenderer<TObject> : HitRenderer public abstract class HitRenderer<TObject> : HitRenderer
where TObject : HitObject where TObject : HitObject
{ {
/// <summary>
/// The Beatmap
/// </summary>
public Beatmap<TObject> Beatmap; public Beatmap<TObject> Beatmap;
protected override Container<Drawable> Content => content;
protected override bool AllObjectsJudged => Playfield.HitObjects.Children.All(h => h.Judgement.Result.HasValue);
protected Playfield<TObject> Playfield;
private Container content;
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);
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
}
/// <summary>
/// Applies the active mods to this HitRenderer.
/// </summary>
/// <param name="mods"></param>
private void applyMods(IEnumerable<Mod> mods)
{
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
mod.Apply(this);
}
/// <summary>
/// Creates a converter to convert Beatmap to a specific mode.
/// </summary>
/// <returns>The Beatmap converter.</returns>
protected abstract IBeatmapConverter<TObject> CreateBeatmapConverter();
/// <summary>
/// Creates a processor to perform post-processing operations
/// on HitObjects in converted Beatmaps.
/// </summary>
/// <returns>The Beatmap processor.</returns>
protected abstract IBeatmapProcessor<TObject> CreateBeatmapProcessor();
}
/// <summary>
/// A derivable HitRenderer that manages the Playfield and HitObjects.
/// </summary>
/// <typeparam name="TObject">The type of HitObject contained by this HitRenderer.</typeparam>
/// <typeparam name="TJudgement">The type of Judgement of DrawableHitObjects contained by this HitRenderer.</typeparam>
public abstract class HitRenderer<TObject, TJudgement> : HitRenderer<TObject>
where TObject : HitObject
where TJudgement : JudgementInfo
{
protected override Container<Drawable> Content => content;
protected override bool AllObjectsJudged => Playfield.HitObjects.Children.All(h => h.Judgement.Result.HasValue);
/// <summary>
/// The playfield.
/// </summary>
protected Playfield<TObject, TJudgement> Playfield;
private Container content;
protected HitRenderer(WorkingBeatmap beatmap)
: base(beatmap)
{
KeyConversionInputManager.Add(Playfield = CreatePlayfield()); KeyConversionInputManager.Add(Playfield = CreatePlayfield());
InputManager.Add(content = new Container InputManager.Add(content = new Container
@ -90,7 +178,7 @@ namespace osu.Game.Modes.UI
{ {
foreach (TObject h in Beatmap.HitObjects) foreach (TObject h in Beatmap.HitObjects)
{ {
DrawableHitObject<TObject> drawableObject = GetVisualRepresentation(h); var drawableObject = GetVisualRepresentation(h);
if (drawableObject == null) if (drawableObject == null)
continue; continue;
@ -103,19 +191,27 @@ namespace osu.Game.Modes.UI
Playfield.PostProcess(); Playfield.PostProcess();
} }
private void applyMods(IEnumerable<Mod> mods) /// <summary>
/// Triggered when an object's Judgement is updated.
/// </summary>
/// <param name="judgedObject">The object that Judgement has been updated for.</param>
private void onJudgement(DrawableHitObject<TObject, TJudgement> judgedObject)
{ {
if (mods == null) TriggerOnJudgement(judgedObject.Judgement);
return; Playfield.OnJudgement(judgedObject);
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
mod.Apply(this);
} }
private void onJudgement(DrawableHitObject<TObject> o, JudgementInfo j) => TriggerOnJudgement(j); /// <summary>
/// Creates a DrawableHitObject from a HitObject.
/// </summary>
/// <param name="h">The HitObject to make drawable.</param>
/// <returns>The DrawableHitObject.</returns>
protected abstract DrawableHitObject<TObject, TJudgement> GetVisualRepresentation(TObject h);
protected abstract DrawableHitObject<TObject> GetVisualRepresentation(TObject h); /// <summary>
protected abstract Playfield<TObject> CreatePlayfield(); /// Creates a Playfield.
protected abstract IBeatmapConverter<TObject> CreateBeatmapConverter(); /// </summary>
/// <returns>The Playfield.</returns>
protected abstract Playfield<TObject, TJudgement> CreatePlayfield();
} }
} }

View File

@ -6,22 +6,23 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Modes.Objects; using osu.Game.Modes.Objects;
using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Objects.Drawables;
using OpenTK; using OpenTK;
using osu.Game.Modes.Judgements;
namespace osu.Game.Modes.UI namespace osu.Game.Modes.UI
{ {
public abstract class Playfield<T> : Container public abstract class Playfield<TObject, TJudgement> : Container
where T : HitObject where TObject : HitObject
where TJudgement : JudgementInfo
{ {
public HitObjectContainer<DrawableHitObject<T>> HitObjects; /// <summary>
/// The HitObjects contained in this Playfield.
public virtual void Add(DrawableHitObject<T> h) => HitObjects.Add(h); /// </summary>
public HitObjectContainer<DrawableHitObject<TObject, TJudgement>> HitObjects;
public override bool Contains(Vector2 screenSpacePos) => true;
internal Container<Drawable> ScaledContent; internal Container<Drawable> ScaledContent;
public override bool Contains(Vector2 screenSpacePos) => true;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private Container<Drawable> content; private Container<Drawable> content;
/// <summary> /// <summary>
@ -43,15 +44,28 @@ namespace osu.Game.Modes.UI
} }
}); });
Add(HitObjects = new HitObjectContainer<DrawableHitObject<T>> Add(HitObjects = new HitObjectContainer<DrawableHitObject<TObject, TJudgement>>
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}); });
} }
public virtual void PostProcess() /// <summary>
{ /// Performs post-processing tasks (if any) after all DrawableHitObjects are loaded into this Playfield.
} /// </summary>
public virtual void PostProcess() { }
/// <summary>
/// Adds a DrawableHitObject to this Playfield.
/// </summary>
/// <param name="h">The DrawableHitObject to add.</param>
public virtual void Add(DrawableHitObject<TObject, TJudgement> h) => HitObjects.Add(h);
/// <summary>
/// Triggered when an object's Judgement is updated.
/// </summary>
/// <param name="judgedObject">The object that Judgement has been updated for.</param>
public virtual void OnJudgement(DrawableHitObject<TObject, TJudgement> judgedObject) { }
private class ScaledContainer : Container private class ScaledContainer : Container
{ {

View File

@ -1,7 +1,6 @@
// 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 OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;

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" />
@ -98,9 +99,29 @@
<Compile Include="Modes\LegacyReplay.cs" /> <Compile Include="Modes\LegacyReplay.cs" />
<Compile Include="Modes\Mods\IApplicableMod.cs" /> <Compile Include="Modes\Mods\IApplicableMod.cs" />
<Compile Include="Modes\Mods\ModType.cs" /> <Compile Include="Modes\Mods\ModType.cs" />
<Compile Include="Modes\Objects\Drawables\ArmedState.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\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" />

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