1
0
mirror of https://github.com/ppy/osu.git synced 2024-09-22 06:47:24 +08:00

Merge branch 'master' into mapping-del

This commit is contained in:
Dean Herbert 2018-05-14 10:58:58 +09:00 committed by GitHub
commit ede4d6dd11
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
107 changed files with 893 additions and 579 deletions

View File

@ -11,7 +11,7 @@ install:
- cmd: git submodule update --init --recursive --depth=5 - cmd: git submodule update --init --recursive --depth=5
- cmd: choco install resharper-clt -y - cmd: choco install resharper-clt -y
- cmd: choco install nvika -y - cmd: choco install nvika -y
- cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.4/CodeFileSanity.exe - cmd: appveyor DownloadFile https://github.com/peppy/CodeFileSanity/releases/download/v0.2.5/CodeFileSanity.exe
before_build: before_build:
- cmd: CodeFileSanity.exe - cmd: CodeFileSanity.exe
- cmd: nuget restore -verbosity quiet - cmd: nuget restore -verbosity quiet

@ -1 +1 @@
Subproject commit 0773d895d9aa0729995cd4a23efc28238e35ceed Subproject commit fac688633b8fcf34ae5d0514c26b03e217161eb4

View File

@ -19,7 +19,7 @@ namespace osu.Desktop.Deploy
{ {
private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); private static string packages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages");
private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe"); private static string nugetPath => Path.Combine(packages, @"nuget.commandline\4.5.1\tools\NuGet.exe");
private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.7.8\tools\Squirrel.exe"); private static string squirrelPath => Path.Combine(packages, @"squirrel.windows\1.8.0\tools\Squirrel.exe");
private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe"; private const string msbuild_path = @"C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\MSBuild\15.0\Bin\MSBuild.exe";
public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"]; public static string StagingFolder = ConfigurationManager.AppSettings["StagingFolder"];
@ -115,7 +115,7 @@ namespace osu.Desktop.Deploy
checkReleaseFiles(); checkReleaseFiles();
write("Running squirrel build..."); write("Running squirrel build...");
runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi"); runCommand(squirrelPath, $"--releasify {stagingPath}\\{nupkgFilename(version)} --framework-version=net471 --setupIcon {iconPath} --icon {iconPath} {codeSigningCmd} --no-msi");
//prune again to clean up before upload. //prune again to clean up before upload.
pruneReleases(); pruneReleases();

View File

@ -12,7 +12,7 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="NuGet.CommandLine" Version="4.5.1" /> <PackageReference Include="NuGet.CommandLine" Version="4.5.1" />
<PackageReference Include="NUnit" Version="3.10.1" /> <PackageReference Include="NUnit" Version="3.10.1" />
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net471'" /> <PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
<PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" /> <PackageReference Include="System.Configuration.ConfigurationManager" Version="4.4.0" />
<PackageReference Include="System.Management.Automation.dll" Version="10.0.10586" /> <PackageReference Include="System.Management.Automation.dll" Version="10.0.10586" />
</ItemGroup> </ItemGroup>

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\osu.Game.props" /> <Import Project="..\osu.Game.props" />
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<TargetFrameworks>net471;netcoreapp2.0</TargetFrameworks> <TargetFrameworks>net471;netcoreapp2.0</TargetFrameworks>
@ -10,8 +10,8 @@
<Title>osu!lazer</Title> <Title>osu!lazer</Title>
<Product>osu!lazer</Product> <Product>osu!lazer</Product>
<ApplicationIcon>lazer.ico</ApplicationIcon> <ApplicationIcon>lazer.ico</ApplicationIcon>
<Version>0.0.0.0</Version> <Version>0.0.0</Version>
<FileVersion>0.0.0.0</FileVersion> <FileVersion>0.0.0</FileVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Label="Defines"> <PropertyGroup Label="Defines">
<DefineConstants Condition="'$(TargetFramework)' == 'net471'">$(DefineConstants);NET_FRAMEWORK</DefineConstants> <DefineConstants Condition="'$(TargetFramework)' == 'net471'">$(DefineConstants);NET_FRAMEWORK</DefineConstants>
@ -31,9 +31,9 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.0.1" />
<PackageReference Include="squirrel.windows" Version="1.7.8" Condition="'$(TargetFramework)' == 'net471'" /> <PackageReference Include="squirrel.windows" Version="1.8.0" Condition="'$(TargetFramework)' == 'net471'" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -14,7 +14,7 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Catch.Tests namespace osu.Game.Rulesets.Catch.Tests
{ {
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue> public class CatchBeatmapConversionTest : BeatmapConversionTest<TestCatchRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; protected override string ResourceAssembly => "osu.Game.Rulesets.Catch";
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
} }
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new CatchBeatmapConverter(); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> public struct ConvertValue : IEquatable<ConvertValue>
@ -64,4 +64,8 @@ namespace osu.Game.Rulesets.Catch.Tests
=> Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience) => Precision.AlmostEquals(StartTime, other.StartTime, conversion_lenience)
&& Precision.AlmostEquals(Position, other.Position, conversion_lenience); && Precision.AlmostEquals(Position, other.Position, conversion_lenience);
} }
public class TestCatchRuleset : CatchRuleset
{
}
} }

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {

View File

@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap var beatmap = new Beatmap
{ {

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
} }
protected override Beatmap CreateBeatmap(Ruleset ruleset) protected override IBeatmap CreateBeatmap(Ruleset ruleset)
{ {
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };

View File

@ -0,0 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Catch.Objects;
namespace osu.Game.Rulesets.Catch.Beatmaps
{
public class CatchBeatmap : Beatmap<CatchHitObject>
{
public override IEnumerable<BeatmapStatistic> GetStatistics()
{
int fruits = HitObjects.Count(s => s is Fruit);
int juiceStreams = HitObjects.Count(s => s is JuiceStream);
int bananaShowers = HitObjects.Count(s => s is BananaShower);
return new[]
{
new BeatmapStatistic
{
Name = @"Fruit Count",
Content = fruits.ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Juice Stream Count",
Content = juiceStreams.ToString(),
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Banana Shower Count",
Content = bananaShowers.ToString(),
Icon = FontAwesome.fa_circle
}
};
}
}
}

View File

@ -13,9 +13,14 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{ {
public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject> public class CatchBeatmapConverter : BeatmapConverter<CatchHitObject>
{ {
public CatchBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap) protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
{ {
var curveData = obj as IHasCurve; var curveData = obj as IHasCurve;
var positionData = obj as IHasXPosition; var positionData = obj as IHasXPosition;
@ -64,5 +69,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
X = positionData.X / CatchPlayfield.BASE_WIDTH X = positionData.X / CatchPlayfield.BASE_WIDTH
}; };
} }
protected override Beatmap<CatchHitObject> CreateBeatmap() => new CatchBeatmap();
} }
} }

View File

@ -12,16 +12,21 @@ using OpenTK;
namespace osu.Game.Rulesets.Catch.Beatmaps namespace osu.Game.Rulesets.Catch.Beatmaps
{ {
public class CatchBeatmapProcessor : BeatmapProcessor<CatchHitObject> public class CatchBeatmapProcessor : BeatmapProcessor
{ {
public override void PostProcess(Beatmap<CatchHitObject> beatmap) public CatchBeatmapProcessor(IBeatmap beatmap)
: base(beatmap)
{ {
initialiseHyperDash(beatmap.HitObjects); }
base.PostProcess(beatmap); public override void PostProcess()
{
initialiseHyperDash((List<CatchHitObject>)Beatmap.HitObjects);
base.PostProcess();
int index = 0; int index = 0;
foreach (var obj in beatmap.HitObjects) foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
obj.IndexInBeatmap = index++; obj.IndexInBeatmap = index++;
} }

View File

@ -2,20 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic; using System.Collections.Generic;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch
{ {
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject> public class CatchDifficultyCalculator : DifficultyCalculator
{ {
public CatchDifficultyCalculator(Beatmap beatmap) : base(beatmap) public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap)
{ {
} }
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0; public override double Calculate(Dictionary<string, double> categoryDifficulty = null) => 0;
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
} }
} }

View File

@ -13,12 +13,15 @@ using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Catch.Beatmaps;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch
{ {
public class CatchRuleset : Ruleset public class CatchRuleset : Ruleset
{ {
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new CatchRulesetContainer(this, beatmap, isForCurrentRuleset); public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new CatchRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap);
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
@ -138,7 +141,7 @@ namespace osu.Game.Rulesets.Catch
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int? LegacyID => 2; public override int? LegacyID => 2;

View File

@ -6,10 +6,11 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using System; using System;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModHardRock : ModHardRock, IApplicableToHitObject<CatchHitObject> public class CatchModHardRock : ModHardRock, IApplicableToHitObject
{ {
public override double ScoreMultiplier => 1.12; public override double ScoreMultiplier => 1.12;
public override bool Ranked => true; public override bool Ranked => true;
@ -17,9 +18,11 @@ namespace osu.Game.Rulesets.Catch.Mods
private float lastStartX; private float lastStartX;
private int lastStartTime; private int lastStartTime;
public void ApplyToHitObject(CatchHitObject hitObject) public void ApplyToHitObject(HitObject hitObject)
{ {
float position = hitObject.X; var catchObject = (CatchHitObject)hitObject;
float position = catchObject.X;
int startTime = (int)hitObject.StartTime; int startTime = (int)hitObject.StartTime;
if (lastStartX == 0) if (lastStartX == 0)
@ -60,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Mods
position += rand; position += rand;
} }
hitObject.X = position; catchObject.X = position;
return; return;
} }
@ -79,7 +82,7 @@ namespace osu.Game.Rulesets.Catch.Mods
} }
} }
hitObject.X = position; catchObject.X = position;
lastStartX = position; lastStartX = position;
lastStartTime = startTime; lastStartTime = startTime;

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Replays
Dashing = dashing; Dashing = dashing;
} }
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
{ {
Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH; Position = legacyFrame.Position.X / CatchPlayfield.BASE_WIDTH;
Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1; Dashing = legacyFrame.ButtonState == ReplayButtonState.Left1;

View File

@ -4,7 +4,6 @@
using osu.Framework.Input; using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable; using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
@ -20,8 +19,8 @@ namespace osu.Game.Rulesets.Catch.UI
{ {
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject> public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
{ {
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap)
{ {
} }
@ -29,10 +28,6 @@ namespace osu.Game.Rulesets.Catch.UI
protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay); protected override ReplayInputHandler CreateReplayInputHandler(Replay replay) => new CatchFramedReplayInputHandler(replay);
protected override BeatmapProcessor<CatchHitObject> CreateBeatmapProcessor() => new CatchBeatmapProcessor();
protected override BeatmapConverter<CatchHitObject> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation); protected override Playfield CreatePlayfield() => new CatchPlayfield(Beatmap.BeatmapInfo.BaseDifficulty, GetVisualRepresentation);
public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new CatchInputManager(Ruleset.RulesetInfo);

View File

@ -14,17 +14,14 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Mania.Tests namespace osu.Game.Rulesets.Mania.Tests
{ {
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue> public class ManiaBeatmapConversionTest : BeatmapConversionTest<TestManiaRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
private bool isForCurrentRuleset;
[NonParallelizable] [NonParallelizable]
[TestCase("basic", false)] [TestCase("basic")]
public void Test(string name, bool isForCurrentRuleset) public new void Test(string name)
{ {
this.isForCurrentRuleset = isForCurrentRuleset;
base.Test(name); base.Test(name);
} }
@ -38,7 +35,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}; };
} }
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new ManiaBeatmapConverter(isForCurrentRuleset, beatmap); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> public struct ConvertValue : IEquatable<ConvertValue>
@ -57,4 +54,8 @@ namespace osu.Game.Rulesets.Mania.Tests
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience) && Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
&& Column == other.Column; && Column == other.Column;
} }
public class TestManiaRuleset : ManiaRuleset
{
}
} }

View File

@ -4,6 +4,7 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mania.UI;
@ -29,5 +30,27 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
{ {
Stages.Add(defaultStage); Stages.Add(defaultStage);
} }
public override IEnumerable<BeatmapStatistic> GetStatistics()
{
int notes = HitObjects.Count(s => s is Note);
int holdnotes = HitObjects.Count(s => s is HoldNote);
return new[]
{
new BeatmapStatistic
{
Name = @"Note Count",
Content = notes.ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Hold Note Count",
Content = holdnotes.ToString(),
Icon = FontAwesome.fa_circle
},
};
}
} }
} }

View File

@ -33,18 +33,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private ManiaBeatmap beatmap; private ManiaBeatmap beatmap;
public ManiaBeatmapConverter(bool isForCurrentRuleset, Beatmap original) public ManiaBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{ {
IsForCurrentRuleset = isForCurrentRuleset; IsForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new ManiaRuleset().RulesetInfo);
var roundedCircleSize = Math.Round(original.BeatmapInfo.BaseDifficulty.CircleSize); var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
var roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty); var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
if (isForCurrentRuleset) if (IsForCurrentRuleset)
TargetColumns = (int)Math.Max(1, roundedCircleSize); TargetColumns = (int)Math.Max(1, roundedCircleSize);
else else
{ {
float percentSliderOrSpinner = (float)original.HitObjects.Count(h => h is IHasEndTime) / original.HitObjects.Count; float percentSliderOrSpinner = (float)beatmap.HitObjects.Count(h => h is IHasEndTime) / beatmap.HitObjects.Count();
if (percentSliderOrSpinner < 0.2) if (percentSliderOrSpinner < 0.2)
TargetColumns = 7; TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5) else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
@ -56,8 +57,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
} }
} }
protected override Beatmap<ManiaHitObject> ConvertBeatmap(Beatmap original) protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
{ {
BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty; BeatmapDifficulty difficulty = original.BeatmapInfo.BaseDifficulty;
int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate); int seed = (int)Math.Round(difficulty.DrainRate + difficulty.CircleSize) * 20 + (int)(difficulty.OverallDifficulty * 41.2) + (int)Math.Round(difficulty.ApproachRate);
@ -68,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns }); protected override Beatmap<ManiaHitObject> CreateBeatmap() => beatmap = new ManiaBeatmap(new StageDefinition { Columns = TargetColumns });
protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) protected override IEnumerable<ManiaHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
{ {
var maniaOriginal = original as ManiaHitObject; var maniaOriginal = original as ManiaHitObject;
if (maniaOriginal != null) if (maniaOriginal != null)
@ -112,7 +114,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <param name="original">The original hit object.</param> /// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param> /// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns> /// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, Beatmap originalBeatmap) private IEnumerable<ManiaHitObject> generateSpecific(HitObject original, IBeatmap originalBeatmap)
{ {
var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap); var generator = new SpecificBeatmapPatternGenerator(random, original, beatmap, lastPattern, originalBeatmap);
@ -128,7 +130,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// <param name="original">The original hit object.</param> /// <param name="original">The original hit object.</param>
/// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param> /// <param name="originalBeatmap">The original beatmap. This is used to look-up any values dependent on a fully-loaded beatmap.</param>
/// <returns>The hit objects generated.</returns> /// <returns>The hit objects generated.</returns>
private IEnumerable<ManiaHitObject> generateConverted(HitObject original, Beatmap originalBeatmap) private IEnumerable<ManiaHitObject> generateConverted(HitObject original, IBeatmap originalBeatmap)
{ {
var endTimeData = original as IHasEndTime; var endTimeData = original as IHasEndTime;
var distanceData = original as IHasDistance; var distanceData = original as IHasDistance;
@ -165,7 +167,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
/// </summary> /// </summary>
private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator private class SpecificBeatmapPatternGenerator : Patterns.Legacy.PatternGenerator
{ {
public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) public SpecificBeatmapPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{ {
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private PatternType convertType; private PatternType convertType;
public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) public DistanceObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{ {
convertType = PatternType.None; convertType = PatternType.None;

View File

@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
{ {
private readonly double endTime; private readonly double endTime;
public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Beatmap originalBeatmap) public EndTimeObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, new Pattern(), originalBeatmap) : base(random, hitObject, beatmap, new Pattern(), originalBeatmap)
{ {
var endtimeData = HitObject as IHasEndTime; var endtimeData = HitObject as IHasEndTime;

View File

@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
private readonly PatternType convertType; private readonly PatternType convertType;
public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, Beatmap originalBeatmap) public HitObjectPatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, double previousTime, Vector2 previousPosition, double density, PatternType lastStair, IBeatmap originalBeatmap)
: base(random, hitObject, beatmap, previousPattern, originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap)
{ {
if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime)); if (previousTime > hitObject.StartTime) throw new ArgumentOutOfRangeException(nameof(previousTime));

View File

@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
/// <summary> /// <summary>
/// The beatmap which <see cref="HitObject"/> is being converted from. /// The beatmap which <see cref="HitObject"/> is being converted from.
/// </summary> /// </summary>
protected readonly Beatmap OriginalBeatmap; protected readonly IBeatmap OriginalBeatmap;
protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, Beatmap originalBeatmap) protected PatternGenerator(FastRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap)
: base(hitObject, beatmap, previousPattern) : base(hitObject, beatmap, previousPattern)
{ {
if (random == null) throw new ArgumentNullException(nameof(random)); if (random == null) throw new ArgumentNullException(nameof(random));
@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
drainTime /= 1000; drainTime /= 1000;
BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty; BeatmapDifficulty difficulty = OriginalBeatmap.BeatmapInfo.BaseDifficulty;
conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count / drainTime * 9f) / 38f * 5f / 1.15; conversionDifficulty = ((difficulty.DrainRate + MathHelper.Clamp(difficulty.ApproachRate, 4, 7)) / 1.5 + OriginalBeatmap.HitObjects.Count() / drainTime * 9f) / 38f * 5f / 1.15;
conversionDifficulty = Math.Min(conversionDifficulty.Value, 12); conversionDifficulty = Math.Min(conversionDifficulty.Value, 12);
return conversionDifficulty.Value; return conversionDifficulty.Value;

View File

@ -10,7 +10,7 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject> internal class ManiaDifficultyCalculator : DifficultyCalculator
{ {
private const double star_scaling_factor = 0.018; private const double star_scaling_factor = 0.018;
@ -31,12 +31,12 @@ namespace osu.Game.Rulesets.Mania
/// </summary> /// </summary>
private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>(); private readonly List<ManiaHitObjectDifficulty> difficultyHitObjects = new List<ManiaHitObjectDifficulty>();
public ManiaDifficultyCalculator(Beatmap beatmap) public ManiaDifficultyCalculator(IBeatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
} }
public ManiaDifficultyCalculator(Beatmap beatmap, Mod[] mods) public ManiaDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
: base(beatmap, mods) : base(beatmap, mods)
{ {
} }
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7; int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
foreach (var hitObject in Beatmap.HitObjects) foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new ManiaHitObjectDifficulty(hitObject, columnCount)); difficultyHitObjects.Add(new ManiaHitObjectDifficulty((ManiaHitObject)hitObject, columnCount));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
@ -140,7 +140,5 @@ namespace osu.Game.Rulesets.Mania
return difficulty; return difficulty;
} }
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, beatmap);
} }
} }

View File

@ -15,12 +15,14 @@ using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Mania.Beatmaps;
namespace osu.Game.Rulesets.Mania namespace osu.Game.Rulesets.Mania
{ {
public class ManiaRuleset : Ruleset public class ManiaRuleset : Ruleset
{ {
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset); public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new ManiaRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap);
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{ {
@ -182,7 +184,7 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods); public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap, mods);
public override int? LegacyID => 3; public override int? LegacyID => 3;

View File

@ -3,19 +3,18 @@
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter<ManiaHitObject> public abstract class ManiaKeyMod : Mod, IApplicableToBeatmapConverter
{ {
public override string ShortenedName => Name; public override string ShortenedName => Name;
public abstract int KeyCount { get; } public abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true; public override bool Ranked => true;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter) public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
{ {
var mbc = (ManiaBeatmapConverter)beatmapConverter; var mbc = (ManiaBeatmapConverter)beatmapConverter;

View File

@ -11,19 +11,23 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter<ManiaHitObject>, IApplicableToRulesetContainer<ManiaHitObject> public class ManiaModDualStages : Mod, IPlayfieldTypeMod, IApplicableToBeatmapConverter, IApplicableToRulesetContainer<ManiaHitObject>
{ {
public override string Name => "Dual Stages"; public override string Name => "Dual Stages";
public override string ShortenedName => "DS"; public override string ShortenedName => "DS";
public override string Description => @"Double the stages, double the fun!"; public override string Description => @"Double the stages, double the fun!";
public override double ScoreMultiplier => 0; public override double ScoreMultiplier => 0;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter) private bool isForCurrentRuleset;
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
{ {
var mbc = (ManiaBeatmapConverter)beatmapConverter; var mbc = (ManiaBeatmapConverter)beatmapConverter;
isForCurrentRuleset = mbc.IsForCurrentRuleset;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps // Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (mbc.IsForCurrentRuleset) if (isForCurrentRuleset)
return; return;
mbc.TargetColumns *= 2; mbc.TargetColumns *= 2;
@ -34,7 +38,7 @@ namespace osu.Game.Rulesets.Mania.Mods
var mrc = (ManiaRulesetContainer)rulesetContainer; var mrc = (ManiaRulesetContainer)rulesetContainer;
// Although this can work, for now let's not allow keymods for mania-specific beatmaps // Although this can work, for now let's not allow keymods for mania-specific beatmaps
if (mrc.IsForCurrentRuleset) if (isForCurrentRuleset)
return; return;
var newDefinitions = new List<StageDefinition>(); var newDefinitions = new List<StageDefinition>();

View File

@ -24,10 +24,10 @@ namespace osu.Game.Rulesets.Mania.Replays
Actions.AddRange(actions); Actions.AddRange(actions);
} }
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
{ {
// We don't need to fully convert, just create the converter // We don't need to fully convert, just create the converter
var converter = new ManiaBeatmapConverter(beatmap.BeatmapInfo.RulesetID == 3, beatmap); var converter = new ManiaBeatmapConverter(beatmap);
// NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling // NB: Via co-op mod, osu-stable can have two stages with floor(col/2) and ceil(col/2) columns. This will need special handling
// elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage. // elsewhere in the game if we do choose to support the old co-op mod anyway. For now, assume that there is only one stage.

View File

@ -36,8 +36,8 @@ namespace osu.Game.Rulesets.Mania.UI
public IEnumerable<BarLine> BarLines; public IEnumerable<BarLine> BarLines;
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap)
{ {
// Generate the bar lines // Generate the bar lines
double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue; double lastObjectTime = (Objects.LastOrDefault() as IHasEndTime)?.EndTime ?? Objects.LastOrDefault()?.StartTime ?? double.MaxValue;
@ -85,8 +85,6 @@ namespace osu.Game.Rulesets.Mania.UI
public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant); public override PassThroughInputManager CreateInputManager() => new ManiaInputManager(Ruleset.RulesetInfo, Variant);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h) protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
{ {
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action; ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;

View File

@ -15,7 +15,7 @@ using OpenTK;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class OsuBeatmapConversionTest : BeatmapConversionTest<ConvertValue> public class OsuBeatmapConversionTest : BeatmapConversionTest<TestOsuRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; protected override string ResourceAssembly => "osu.Game.Rulesets.Osu";
@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}; };
} }
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new OsuBeatmapConverter(); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> public struct ConvertValue : IEquatable<ConvertValue>
@ -67,4 +67,8 @@ namespace osu.Game.Rulesets.Osu.Tests
&& Precision.AlmostEquals(EndX, other.EndX, conversion_lenience) && Precision.AlmostEquals(EndX, other.EndX, conversion_lenience)
&& Precision.AlmostEquals(EndY, other.EndY, conversion_lenience); && Precision.AlmostEquals(EndY, other.EndY, conversion_lenience);
} }
public class TestOsuRuleset : OsuRuleset
{
}
} }

View File

@ -0,0 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Beatmaps
{
public class OsuBeatmap : Beatmap<OsuHitObject>
{
public override IEnumerable<BeatmapStatistic> GetStatistics()
{
int circles = HitObjects.Count(c => c is HitCircle);
int sliders = HitObjects.Count(s => s is Slider);
int spinners = HitObjects.Count(s => s is Spinner);
return new[]
{
new BeatmapStatistic
{
Name = @"Circle Count",
Content = circles.ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.ToString(),
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Spinner Count",
Content = spinners.ToString(),
Icon = FontAwesome.fa_circle
}
};
}
}
}

View File

@ -14,9 +14,14 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{ {
internal class OsuBeatmapConverter : BeatmapConverter<OsuHitObject> internal class OsuBeatmapConverter : BeatmapConverter<OsuHitObject>
{ {
public OsuBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) };
protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, Beatmap beatmap) protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)
{ {
var curveData = original as IHasCurve; var curveData = original as IHasCurve;
var endTimeData = original as IHasEndTime; var endTimeData = original as IHasEndTime;
@ -60,5 +65,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
}; };
} }
} }
protected override Beatmap<OsuHitObject> CreateBeatmap() => new OsuBeatmap();
} }
} }

View File

@ -8,12 +8,17 @@ using osu.Game.Rulesets.Osu.Objects;
namespace osu.Game.Rulesets.Osu.Beatmaps namespace osu.Game.Rulesets.Osu.Beatmaps
{ {
internal class OsuBeatmapProcessor : BeatmapProcessor<OsuHitObject> internal class OsuBeatmapProcessor : BeatmapProcessor
{ {
public override void PostProcess(Beatmap<OsuHitObject> beatmap) public OsuBeatmapProcessor(IBeatmap beatmap)
: base(beatmap)
{ {
applyStacking(beatmap); }
base.PostProcess(beatmap);
public override void PostProcess()
{
applyStacking((Beatmap<OsuHitObject>)Beatmap);
base.PostProcess();
} }
private void applyStacking(Beatmap<OsuHitObject> beatmap) private void applyStacking(Beatmap<OsuHitObject> beatmap)

View File

@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
public class OsuEditRulesetContainer : OsuRulesetContainer public class OsuEditRulesetContainer : OsuRulesetContainer
{ {
public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap)
{ {
} }

View File

@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Edit
{ {
} }
protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap, true); protected override RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => new OsuEditRulesetContainer(ruleset, beatmap);
protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[] protected override IReadOnlyList<ICompositionTool> CompositionTools => new ICompositionTool[]
{ {

View File

@ -5,20 +5,23 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using OpenTK; using OpenTK;
namespace osu.Game.Rulesets.Osu.Mods namespace osu.Game.Rulesets.Osu.Mods
{ {
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject> public class OsuModHardRock : ModHardRock, IApplicableToHitObject
{ {
public override double ScoreMultiplier => 1.06; public override double ScoreMultiplier => 1.06;
public override bool Ranked => true; public override bool Ranked => true;
public void ApplyToHitObject(OsuHitObject hitObject) public void ApplyToHitObject(HitObject hitObject)
{ {
hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y); var osuObject = (OsuHitObject)hitObject;
osuObject.Position = new Vector2(osuObject.Position.X, OsuPlayfield.BASE_SIZE.Y - osuObject.Y);
var slider = hitObject as Slider; var slider = hitObject as Slider;
if (slider == null) if (slider == null)

View File

@ -5,36 +5,30 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing; using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
using osu.Game.Rulesets.Osu.OsuDifficulty.Skills; using osu.Game.Rulesets.Osu.OsuDifficulty.Skills;
namespace osu.Game.Rulesets.Osu.OsuDifficulty namespace osu.Game.Rulesets.Osu.OsuDifficulty
{ {
public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject> public class OsuDifficultyCalculator : DifficultyCalculator
{ {
private const int section_length = 400; private const int section_length = 400;
private const double difficulty_multiplier = 0.0675; private const double difficulty_multiplier = 0.0675;
public OsuDifficultyCalculator(Beatmap beatmap) public OsuDifficultyCalculator(IBeatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
} }
public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods) public OsuDifficultyCalculator(IBeatmap beatmap, Mod[] mods)
: base(beatmap, mods) : base(beatmap, mods)
{ {
} }
protected override void PreprocessHitObjects()
{
new OsuBeatmapProcessor().PostProcess(Beatmap);
}
public override double Calculate(Dictionary<string, double> categoryDifficulty = null) public override double Calculate(Dictionary<string, double> categoryDifficulty = null)
{ {
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate); OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap((List<OsuHitObject>)Beatmap.HitObjects, TimeRate);
Skill[] skills = Skill[] skills =
{ {
new Aim(), new Aim(),
@ -72,7 +66,5 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
return starRating; return starRating;
} }
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter();
} }
} }

View File

@ -9,7 +9,6 @@ using osu.Game.Rulesets.Osu.OsuDifficulty;
using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
@ -17,17 +16,18 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Scoring;
using osu.Game.Rulesets.Osu.Edit; using osu.Game.Rulesets.Osu.Edit;
using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Osu.Beatmaps;
namespace osu.Game.Rulesets.Osu namespace osu.Game.Rulesets.Osu
{ {
public class OsuRuleset : Ruleset public class OsuRuleset : Ruleset
{ {
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new OsuRulesetContainer(this, beatmap, isForCurrentRuleset); public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new OsuRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap);
public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap);
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
@ -37,36 +37,6 @@ namespace osu.Game.Rulesets.Osu
new KeyBinding(InputKey.MouseRight, OsuAction.RightButton), new KeyBinding(InputKey.MouseRight, OsuAction.RightButton),
}; };
public override IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap)
{
IEnumerable<HitObject> hitObjects = beatmap.Beatmap.HitObjects;
IEnumerable<HitObject> circles = hitObjects.Where(c => !(c is IHasEndTime));
IEnumerable<HitObject> sliders = hitObjects.Where(s => s is IHasCurve);
IEnumerable<HitObject> spinners = hitObjects.Where(s => s is IHasEndTime && !(s is IHasCurve));
return new[]
{
new BeatmapStatistic
{
Name = @"Circle Count",
Content = circles.Count().ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.Count().ToString(),
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Spinner Count",
Content = spinners.Count().ToString(),
Icon = FontAwesome.fa_circle
}
};
}
public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) public override IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods)
{ {
if (mods.HasFlag(LegacyMods.Nightcore)) if (mods.HasFlag(LegacyMods.Nightcore))
@ -181,9 +151,9 @@ namespace osu.Game.Rulesets.Osu
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods); public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
public override PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score); public override PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => new OsuPerformanceCalculator(this, beatmap, score);
public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this);

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Replays
Actions.AddRange(actions); Actions.AddRange(actions);
} }
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
{ {
Position = legacyFrame.Position; Position = legacyFrame.Position;
if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton); if (legacyFrame.MouseLeft) Actions.Add(OsuAction.LeftButton);

View File

@ -6,14 +6,13 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Scoring namespace osu.Game.Rulesets.Osu.Scoring
{ {
public class OsuPerformanceCalculator : PerformanceCalculator<OsuHitObject> public class OsuPerformanceCalculator : PerformanceCalculator
{ {
private readonly int countHitCircles; private readonly int countHitCircles;
private readonly int beatmapMaxCombo; private readonly int beatmapMaxCombo;
@ -27,13 +26,14 @@ namespace osu.Game.Rulesets.Osu.Scoring
private int count50; private int count50;
private int countMiss; private int countMiss;
public OsuPerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) public OsuPerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
: base(ruleset, beatmap, score) : base(ruleset, beatmap, score)
{ {
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle); countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
beatmapMaxCombo = Beatmap.HitObjects.Count; beatmapMaxCombo = Beatmap.HitObjects.Count();
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count) + 1; // Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
} }
public override double Calculate(Dictionary<string, double> categoryRatings = null) public override double Calculate(Dictionary<string, double> categoryRatings = null)
@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
aimValue *= approachRateFactor; aimValue *= approachRateFactor;
if (mods.Any(h => h is OsuModHidden)) if (mods.Any(h => h is OsuModHidden))
aimValue *= 1.18f; aimValue *= 1.03f;
if (mods.Any(h => h is OsuModFlashlight)) if (mods.Any(h => h is OsuModFlashlight))
{ {
@ -153,6 +153,9 @@ namespace osu.Game.Rulesets.Osu.Scoring
if (beatmapMaxCombo > 0) if (beatmapMaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.18f;
// Scale the speed value with accuracy _slightly_ // Scale the speed value with accuracy _slightly_
speedValue *= 0.5f + accuracy / 2.0f; speedValue *= 0.5f + accuracy / 2.0f;
// It is important to also consider accuracy difficulty when doing that // It is important to also consider accuracy difficulty when doing that
@ -193,7 +196,5 @@ namespace osu.Game.Rulesets.Osu.Scoring
private double totalHits => count300 + count100 + count50 + countMiss; private double totalHits => count300 + count100 + count50 + countMiss;
private double totalSuccessfulHits => count300 + count100 + count50; private double totalSuccessfulHits => count300 + count100 + count50;
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
} }
} }

View File

@ -7,7 +7,6 @@ using OpenTK;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Input.Handlers; using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
@ -21,17 +20,13 @@ namespace osu.Game.Rulesets.Osu.UI
{ {
public class OsuRulesetContainer : RulesetContainer<OsuHitObject> public class OsuRulesetContainer : RulesetContainer<OsuHitObject>
{ {
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap)
{ {
} }
public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(this);
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
protected override BeatmapProcessor<OsuHitObject> CreateBeatmapProcessor() => new OsuBeatmapProcessor();
protected override Playfield CreatePlayfield() => new OsuPlayfield(); protected override Playfield CreatePlayfield() => new OsuPlayfield();
public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo);

View File

@ -14,18 +14,15 @@ using osu.Game.Tests.Beatmaps;
namespace osu.Game.Rulesets.Taiko.Tests namespace osu.Game.Rulesets.Taiko.Tests
{ {
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue> public class TaikoBeatmapConversionTest : BeatmapConversionTest<TestTaikoRuleset, ConvertValue>
{ {
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko"; protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
private bool isForCurrentRuleset;
[NonParallelizable] [NonParallelizable]
[TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")] [TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")]
[TestCase("slider-generating-drumroll", false)] [TestCase("slider-generating-drumroll", false)]
public void Test(string name, bool isForCurrentRuleset) public new void Test(string name)
{ {
this.isForCurrentRuleset = isForCurrentRuleset;
base.Test(name); base.Test(name);
} }
@ -43,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
}; };
} }
protected override IBeatmapConverter CreateConverter(Beatmap beatmap) => new TaikoBeatmapConverter(isForCurrentRuleset); protected override IBeatmapConverter CreateConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
} }
public struct ConvertValue : IEquatable<ConvertValue> public struct ConvertValue : IEquatable<ConvertValue>
@ -70,4 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
&& IsSwell == other.IsSwell && IsSwell == other.IsSwell
&& IsStrong == other.IsStrong; && IsStrong == other.IsStrong;
} }
public class TestTaikoRuleset : TaikoRuleset
{
}
} }

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
private Container playfieldContainer; private Container playfieldContainer;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(RulesetStore rulesets) private void load()
{ {
AddStep("Hit!", () => addHitJudgement(false)); AddStep("Hit!", () => addHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true)); AddStep("Kiai hit", () => addHitJudgement(true));
@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Title = @"Sample Beatmap", Title = @"Sample Beatmap",
AuthorString = @"peppy", AuthorString = @"peppy",
}, },
Ruleset = new TaikoRuleset().RulesetInfo
}, },
ControlPointInfo = controlPointInfo ControlPointInfo = controlPointInfo
}); });
@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
Height = 768, Height = 768,
Clock = new FramedClock(rateAdjustClock), Clock = new FramedClock(rateAdjustClock),
Children = new[] { rulesetContainer = new TaikoRulesetContainer(rulesets.GetRuleset(1).CreateInstance(), beatmap, true) } Children = new[] { rulesetContainer = new TaikoRulesetContainer(new TaikoRuleset(), beatmap) }
}); });
} }

View File

@ -0,0 +1,43 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Taiko.Objects;
namespace osu.Game.Rulesets.Taiko.Beatmaps
{
public class TaikoBeatmap : Beatmap<TaikoHitObject>
{
public override IEnumerable<BeatmapStatistic> GetStatistics()
{
int hits = HitObjects.Count(s => s is Hit);
int drumrolls = HitObjects.Count(s => s is DrumRoll);
int swells = HitObjects.Count(s => s is Swell);
return new[]
{
new BeatmapStatistic
{
Name = @"Hit Count",
Content = hits.ToString(),
Icon = FontAwesome.fa_circle_o
},
new BeatmapStatistic
{
Name = @"Drumroll Count",
Content = drumrolls.ToString(),
Icon = FontAwesome.fa_circle
},
new BeatmapStatistic
{
Name = @"Swell Count",
Content = swells.ToString(),
Icon = FontAwesome.fa_circle
}
};
}
}
}

View File

@ -42,12 +42,13 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) }; protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(HitObject) };
public TaikoBeatmapConverter(bool isForCurrentRuleset) public TaikoBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{ {
this.isForCurrentRuleset = isForCurrentRuleset; isForCurrentRuleset = beatmap.BeatmapInfo.Ruleset.Equals(new TaikoRuleset().RulesetInfo);
} }
protected override Beatmap<TaikoHitObject> ConvertBeatmap(Beatmap original) protected override Beatmap<TaikoHitObject> ConvertBeatmap(IBeatmap original)
{ {
// Rewrite the beatmap info to add the slider velocity multiplier // Rewrite the beatmap info to add the slider velocity multiplier
BeatmapInfo info = original.BeatmapInfo.DeepClone(); BeatmapInfo info = original.BeatmapInfo.DeepClone();
@ -70,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
return converted; return converted;
} }
protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, Beatmap beatmap) protected override IEnumerable<TaikoHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)
{ {
var distanceData = obj as IHasDistance; var distanceData = obj as IHasDistance;
var repeatsData = obj as IHasRepeats; var repeatsData = obj as IHasRepeats;
@ -196,5 +197,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
} }
} }
} }
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
} }
} }

View File

@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Taiko.Replays
Actions.AddRange(actions); Actions.AddRange(actions);
} }
public void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap) public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
{ {
if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim); if (legacyFrame.MouseRight1) Actions.Add(TaikoAction.LeftRim);
if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim); if (legacyFrame.MouseRight2) Actions.Add(TaikoAction.RightRim);

View File

@ -2,14 +2,13 @@
// 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.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
namespace osu.Game.Rulesets.Taiko namespace osu.Game.Rulesets.Taiko
{ {
internal class TaikoDifficultyCalculator : DifficultyCalculator<TaikoHitObject> internal class TaikoDifficultyCalculator : DifficultyCalculator
{ {
private const double star_scaling_factor = 0.04125; private const double star_scaling_factor = 0.04125;
@ -30,7 +29,7 @@ namespace osu.Game.Rulesets.Taiko
/// </summary> /// </summary>
private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>(); private readonly List<TaikoHitObjectDifficulty> difficultyHitObjects = new List<TaikoHitObjectDifficulty>();
public TaikoDifficultyCalculator(Beatmap beatmap) public TaikoDifficultyCalculator(IBeatmap beatmap)
: base(beatmap) : base(beatmap)
{ {
} }
@ -41,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko
difficultyHitObjects.Clear(); difficultyHitObjects.Clear();
foreach (var hitObject in Beatmap.HitObjects) foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject)); difficultyHitObjects.Add(new TaikoHitObjectDifficulty((TaikoHitObject)hitObject));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure. // Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime)); difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
@ -132,7 +131,5 @@ namespace osu.Game.Rulesets.Taiko
return difficulty; return difficulty;
} }
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true);
} }
} }

View File

@ -13,12 +13,14 @@ using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Taiko.Beatmaps;
namespace osu.Game.Rulesets.Taiko namespace osu.Game.Rulesets.Taiko
{ {
public class TaikoRuleset : Ruleset public class TaikoRuleset : Ruleset
{ {
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new TaikoRulesetContainer(this, beatmap, isForCurrentRuleset); public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap) => new TaikoRulesetContainer(this, beatmap);
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new TaikoBeatmapConverter(beatmap);
public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[] public override IEnumerable<KeyBinding> GetDefaultKeyBindings(int variant = 0) => new[]
{ {
@ -140,7 +142,7 @@ namespace osu.Game.Rulesets.Taiko
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o }; public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap); public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
public override int? LegacyID => 1; public override int? LegacyID => 1;

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring; using osu.Game.Rulesets.Taiko.Scoring;
@ -24,8 +23,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{ {
public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject> public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject>
{ {
public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap)
{ {
} }
@ -93,8 +92,6 @@ namespace osu.Game.Rulesets.Taiko.UI
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this); public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset);
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo); public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo) protected override Playfield CreatePlayfield() => new TaikoPlayfield(Beatmap.ControlPointInfo)

View File

@ -275,13 +275,13 @@ namespace osu.Game.Tests.Beatmaps.IO
Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID)); Assert.IsTrue(set.Beatmaps.Any(c => c.OnlineBeatmapID == b.OnlineBeatmapID));
Assert.IsTrue(set.Beatmaps.Count > 0); Assert.IsTrue(set.Beatmaps.Count > 0);
var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap; var beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 0))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0); Assert.IsTrue(beatmap?.HitObjects.Any() == true);
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap; beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 1))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0); Assert.IsTrue(beatmap?.HitObjects.Any() == true);
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap; beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 2))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0); Assert.IsTrue(beatmap?.HitObjects.Any() == true);
beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap; beatmap = store.GetWorkingBeatmap(set.Beatmaps.First(b => b.RulesetID == 3))?.Beatmap;
Assert.IsTrue(beatmap?.HitObjects.Count > 0); Assert.IsTrue(beatmap?.HitObjects.Any() == true);
} }
private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000) private void waitForOrAssert(Func<bool> result, string failureMessage, int timeout = 60000)

View File

@ -12,8 +12,12 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Catch;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Taiko;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Beatmaps;
@ -24,7 +28,7 @@ namespace osu.Game.Tests.Visual
{ {
private RulesetStore rulesets; private RulesetStore rulesets;
private TestBeatmapInfoWedge infoWedge; private TestBeatmapInfoWedge infoWedge;
private readonly List<Beatmap> beatmaps = new List<Beatmap>(); private readonly List<IBeatmap> beatmaps = new List<IBeatmap>();
private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); private readonly Bindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -72,13 +76,23 @@ namespace osu.Game.Tests.Visual
selectBeatmap(testBeatmap); selectBeatmap(testBeatmap);
testBeatmapLabels(ruleset);
// TODO: adjust cases once more info is shown for other gamemodes // TODO: adjust cases once more info is shown for other gamemodes
switch (ruleset) switch (ruleset)
{ {
case OsuRuleset osu: case OsuRuleset _:
testOsuBeatmap(osu);
testInfoLabels(5); testInfoLabels(5);
break; break;
case TaikoRuleset _:
testInfoLabels(5);
break;
case CatchRuleset _:
testInfoLabels(5);
break;
case ManiaRuleset _:
testInfoLabels(4);
break;
default: default:
testInfoLabels(2); testInfoLabels(2);
break; break;
@ -88,7 +102,7 @@ namespace osu.Game.Tests.Visual
testNullBeatmap(); testNullBeatmap();
} }
private void testOsuBeatmap(OsuRuleset ruleset) private void testBeatmapLabels(Ruleset ruleset)
{ {
AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version"); AddAssert("check version", () => infoWedge.Info.VersionLabel.Text == $"{ruleset.ShortName}Version");
AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title"); AddAssert("check title", () => infoWedge.Info.TitleLabel.Text == $"{ruleset.ShortName}Source — {ruleset.ShortName}Title");
@ -112,7 +126,7 @@ namespace osu.Game.Tests.Visual
AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any()); AddAssert("check no infolabels", () => !infoWedge.Info.InfoLabelContainer.Children.Any());
} }
private void selectBeatmap(Beatmap b) private void selectBeatmap(IBeatmap b)
{ {
BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null; BeatmapInfoWedge.BufferedWedgeInfo infoBefore = null;
@ -134,11 +148,11 @@ namespace osu.Game.Tests.Visual
}); });
} }
private Beatmap createTestBeatmap(RulesetInfo ruleset) private IBeatmap createTestBeatmap(RulesetInfo ruleset)
{ {
List<HitObject> objects = new List<HitObject>(); List<HitObject> objects = new List<HitObject>();
for (double i = 0; i < 50000; i += 1000) for (double i = 0; i < 50000; i += 1000)
objects.Add(new HitObject { StartTime = i }); objects.Add(new TestHitObject { StartTime = i });
return new Beatmap return new Beatmap
{ {
@ -153,7 +167,8 @@ namespace osu.Game.Tests.Visual
}, },
Ruleset = ruleset, Ruleset = ruleset,
StarDifficulty = 6, StarDifficulty = 6,
Version = $"{ruleset.ShortName}Version" Version = $"{ruleset.ShortName}Version",
BaseDifficulty = new BeatmapDifficulty()
}, },
HitObjects = objects HitObjects = objects
}; };
@ -163,5 +178,12 @@ namespace osu.Game.Tests.Visual
{ {
public new BufferedWedgeInfo Info => base.Info; public new BufferedWedgeInfo Info => base.Info;
} }
private class TestHitObject : HitObject, IHasPosition
{
public float X { get; } = 0;
public float Y { get; } = 0;
public Vector2 Position { get; } = Vector2.Zero;
}
} }
} }

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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.Framework.Timing;
using osu.Game.Beatmaps.Timing; using osu.Game.Beatmaps.Timing;
using System.Collections.Generic; using System.Collections.Generic;
using NUnit.Framework; using NUnit.Framework;
@ -16,8 +15,6 @@ namespace osu.Game.Tests.Visual
public TestCaseBreakOverlay() public TestCaseBreakOverlay()
{ {
Clock = new FramedClock();
Child = breakOverlay = new BreakOverlay(true); Child = breakOverlay = new BreakOverlay(true);
AddStep("2s break", () => startBreak(2000)); AddStep("2s break", () => startBreak(2000));

View File

@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Multiplayer; using osu.Game.Screens.Multi.Components;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual

View File

@ -332,7 +332,7 @@ namespace osu.Game.Tests.Visual
private readonly Drawable tracker; private readonly Drawable tracker;
public TimingPointVisualiser(Beatmap beatmap, double length) public TimingPointVisualiser(IBeatmap beatmap, double length)
{ {
this.length = length; this.length = length;

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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 System.ComponentModel; using System.ComponentModel;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -17,6 +18,7 @@ using osu.Game.Graphics.UserInterface;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.UI;
using OpenTK.Graphics; using OpenTK.Graphics;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -24,6 +26,19 @@ namespace osu.Game.Tests.Visual
[Description("mod select and icon display")] [Description("mod select and icon display")]
public class TestCaseMods : OsuTestCase public class TestCaseMods : OsuTestCase
{ {
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ModSelectOverlay),
typeof(ModDisplay),
typeof(ModSection),
typeof(ModIcon),
typeof(ModButton),
typeof(ModButtonEmpty),
typeof(DifficultyReductionSection),
typeof(DifficultyIncreaseSection),
typeof(SpecialSection),
};
private const string unranked_suffix = " (Unranked)"; private const string unranked_suffix = " (Unranked)";
private RulesetStore rulesets; private RulesetStore rulesets;
@ -66,7 +81,8 @@ namespace osu.Game.Tests.Visual
Ruleset ruleset = rulesetInfo.CreateInstance(); Ruleset ruleset = rulesetInfo.CreateInstance();
AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo); AddStep($"switch to {ruleset.Description}", () => modSelect.Ruleset.Value = rulesetInfo);
switch (ruleset) { switch (ruleset)
{
case OsuRuleset or: case OsuRuleset or:
testOsuMods(or); testOsuMods(or);
break; break;

View File

@ -18,7 +18,7 @@ namespace osu.Game.Tests.Visual
// We create a dummy RulesetContainer just to get the replay - we don't want to use mods here // We create a dummy RulesetContainer just to get the replay - we don't want to use mods here
// to simulate setting a replay rather than having the replay already set for us // to simulate setting a replay rather than having the replay already set for us
beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() }); beatmap.Mods.Value = beatmap.Mods.Value.Concat(new[] { ruleset.GetAutoplayMod() });
var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap, beatmap.BeatmapInfo.Ruleset.Equals(ruleset.RulesetInfo)); var dummyRulesetContainer = ruleset.CreateRulesetContainerWith(beatmap);
// We have the replay // We have the replay
var replay = dummyRulesetContainer.Replay; var replay = dummyRulesetContainer.Replay;

View File

@ -7,7 +7,7 @@ using osu.Framework.Graphics;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Screens.Multiplayer; using osu.Game.Screens.Multi.Components;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual

View File

@ -15,21 +15,27 @@ namespace osu.Game.Beatmaps
/// <summary> /// <summary>
/// A Beatmap containing converted HitObjects. /// A Beatmap containing converted HitObjects.
/// </summary> /// </summary>
public class Beatmap<T> : IJsonSerializable public class Beatmap<T> : IBeatmap
where T : HitObject where T : HitObject
{ {
public BeatmapInfo BeatmapInfo = new BeatmapInfo(); public BeatmapInfo BeatmapInfo { get; set; } = new BeatmapInfo
public ControlPointInfo ControlPointInfo = new ControlPointInfo(); {
public List<BreakPeriod> Breaks = new List<BreakPeriod>(); Metadata = new BeatmapMetadata
{
Artist = @"Unknown",
Title = @"Unknown",
AuthorString = @"Unknown Creator",
},
Version = @"Normal",
BaseDifficulty = new BeatmapDifficulty()
};
[JsonIgnore] [JsonIgnore]
public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata; public BeatmapMetadata Metadata => BeatmapInfo?.Metadata ?? BeatmapInfo?.BeatmapSet?.Metadata;
/// <summary> public ControlPointInfo ControlPointInfo { get; set; } = new ControlPointInfo();
/// The HitObjects this Beatmap contains.
/// </summary> public List<BreakPeriod> Breaks { get; set; } = new List<BreakPeriod>();
[JsonConverter(typeof(TypedListConverter<HitObject>))]
public List<T> HitObjects = new List<T>();
/// <summary> /// <summary>
/// Total amount of break time in the beatmap. /// Total amount of break time in the beatmap.
@ -38,51 +44,28 @@ namespace osu.Game.Beatmaps
public double TotalBreakTime => Breaks.Sum(b => b.Duration); public double TotalBreakTime => Breaks.Sum(b => b.Duration);
/// <summary> /// <summary>
/// Constructs a new beatmap. /// The HitObjects this Beatmap contains.
/// </summary> /// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param> [JsonConverter(typeof(TypedListConverter<HitObject>))]
public Beatmap(Beatmap<T> original = null) public List<T> HitObjects = new List<T>();
{
BeatmapInfo = original?.BeatmapInfo.DeepClone() ?? BeatmapInfo;
ControlPointInfo = original?.ControlPointInfo ?? ControlPointInfo;
Breaks = original?.Breaks ?? Breaks;
HitObjects = original?.HitObjects ?? HitObjects;
if (original == null && Metadata == null) IEnumerable<HitObject> IBeatmap.HitObjects => HitObjects;
{
// we may have no metadata in cases we weren't sourced from the database. public virtual IEnumerable<BeatmapStatistic> GetStatistics() => Enumerable.Empty<BeatmapStatistic>();
// let's fill it (and other related fields) so we don't need to null-check it in future usages.
BeatmapInfo = new BeatmapInfo IBeatmap IBeatmap.Clone() => Clone();
{
Metadata = new BeatmapMetadata public Beatmap<T> Clone()
{ {
Artist = @"Unknown", var newInstance = (Beatmap<T>)MemberwiseClone();
Title = @"Unknown", newInstance.BeatmapInfo = BeatmapInfo.DeepClone();
AuthorString = @"Unknown Creator",
}, return newInstance;
Version = @"Normal",
BaseDifficulty = new BeatmapDifficulty()
};
}
} }
} }
/// <summary>
/// A Beatmap containing un-converted HitObjects.
/// </summary>
public class Beatmap : Beatmap<HitObject> public class Beatmap : Beatmap<HitObject>
{ {
/// <summary> public Beatmap Clone() => (Beatmap)base.Clone();
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
public Beatmap(Beatmap original)
: base(original)
{
}
public Beatmap()
{
}
} }
} }

View File

@ -22,32 +22,34 @@ namespace osu.Game.Beatmaps
remove => ObjectConverted -= value; remove => ObjectConverted -= value;
} }
/// <summary> public IBeatmap Beatmap { get; }
/// Checks if a Beatmap can be converted using this Beatmap Converter.
/// </summary>
/// <param name="beatmap">The Beatmap to check.</param>
/// <returns>Whether the Beatmap can be converted using this Beatmap Converter.</returns>
public bool CanConvert(Beatmap beatmap) => ValidConversionTypes.All(t => beatmap.HitObjects.Any(t.IsInstanceOfType));
/// <summary> protected BeatmapConverter(IBeatmap beatmap)
/// Converts a Beatmap using this Beatmap Converter.
/// </summary>
/// <param name="original">The un-converted Beatmap.</param>
/// <returns>The converted Beatmap.</returns>
public Beatmap<T> Convert(Beatmap original)
{ {
// We always operate on a clone of the original beatmap, to not modify it game-wide Beatmap = beatmap;
return ConvertBeatmap(new Beatmap(original));
} }
void IBeatmapConverter.Convert(Beatmap original) => Convert(original); /// <summary>
/// Whether <see cref="Beatmap"/> can be converted by this <see cref="BeatmapConverter{T}"/>.
/// </summary>
public bool CanConvert => !Beatmap.HitObjects.Any() || ValidConversionTypes.All(t => Beatmap.HitObjects.Any(t.IsInstanceOfType));
/// <summary>
/// Converts <see cref="Beatmap"/>.
/// </summary>
/// <returns>The converted Beatmap.</returns>
public IBeatmap Convert()
{
// We always operate on a clone of the original beatmap, to not modify it game-wide
return ConvertBeatmap(Beatmap.Clone());
}
/// <summary> /// <summary>
/// Performs the conversion of a Beatmap using this Beatmap Converter. /// Performs the conversion of a Beatmap using this Beatmap Converter.
/// </summary> /// </summary>
/// <param name="original">The un-converted Beatmap.</param> /// <param name="original">The un-converted Beatmap.</param>
/// <returns>The converted Beatmap.</returns> /// <returns>The converted Beatmap.</returns>
protected virtual Beatmap<T> ConvertBeatmap(Beatmap original) protected virtual Beatmap<T> ConvertBeatmap(IBeatmap original)
{ {
var beatmap = CreateBeatmap(); var beatmap = CreateBeatmap();
@ -67,7 +69,7 @@ namespace osu.Game.Beatmaps
/// <param name="original">The hit object to convert.</param> /// <param name="original">The hit object to convert.</param>
/// <param name="beatmap">The un-converted Beatmap.</param> /// <param name="beatmap">The un-converted Beatmap.</param>
/// <returns>The converted hit object.</returns> /// <returns>The converted hit object.</returns>
private IEnumerable<T> convert(HitObject original, Beatmap beatmap) private IEnumerable<T> convert(HitObject original, IBeatmap beatmap)
{ {
// Check if the hitobject is already the converted type // Check if the hitobject is already the converted type
T tObject = original as T; T tObject = original as T;
@ -107,6 +109,6 @@ namespace osu.Game.Beatmaps
/// <param name="original">The hit object to convert.</param> /// <param name="original">The hit object to convert.</param>
/// <param name="beatmap">The un-converted Beatmap.</param> /// <param name="beatmap">The un-converted Beatmap.</param>
/// <returns>The converted hit object.</returns> /// <returns>The converted hit object.</returns>
protected abstract IEnumerable<T> ConvertHitObject(HitObject original, Beatmap beatmap); protected abstract IEnumerable<T> ConvertHitObject(HitObject original, IBeatmap beatmap);
} }
} }

View File

@ -9,7 +9,9 @@ using System.Linq.Expressions;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
@ -333,7 +335,7 @@ namespace osu.Game.Beatmaps
ms.Position = 0; ms.Position = 0;
var decoder = Decoder.GetDecoder<Beatmap>(sr); var decoder = Decoder.GetDecoder<Beatmap>(sr);
Beatmap beatmap = decoder.Decode(sr); IBeatmap beatmap = decoder.Decode(sr);
beatmap.BeatmapInfo.Path = name; beatmap.BeatmapInfo.Path = name;
beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash(); beatmap.BeatmapInfo.Hash = ms.ComputeSHA2Hash();
@ -341,9 +343,16 @@ namespace osu.Game.Beatmaps
RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID); RulesetInfo ruleset = rulesets.GetRuleset(beatmap.BeatmapInfo.RulesetID);
// TODO: this should be done in a better place once we actually need to dynamically update it.
beatmap.BeatmapInfo.Ruleset = ruleset; beatmap.BeatmapInfo.Ruleset = ruleset;
beatmap.BeatmapInfo.StarDifficulty = ruleset?.CreateInstance()?.CreateDifficultyCalculator(beatmap).Calculate() ?? 0;
if (ruleset != null)
{
// TODO: this should be done in a better place once we actually need to dynamically update it.
var converted = new DummyConversionBeatmap(beatmap).GetPlayableBeatmap(ruleset);
beatmap.BeatmapInfo.StarDifficulty = ruleset.CreateInstance().CreateDifficultyCalculator(converted).Calculate();
}
else
beatmap.BeatmapInfo.StarDifficulty = 0;
beatmapInfos.Add(beatmap.BeatmapInfo); beatmapInfos.Add(beatmap.BeatmapInfo);
} }
@ -351,5 +360,23 @@ namespace osu.Game.Beatmaps
return beatmapInfos; return beatmapInfos;
} }
/// <summary>
/// A dummy WorkingBeatmap for the purpose of retrieving a beatmap for star difficulty calculation.
/// </summary>
private class DummyConversionBeatmap : WorkingBeatmap
{
private readonly IBeatmap beatmap;
public DummyConversionBeatmap(IBeatmap beatmap)
: base(beatmap.BeatmapInfo)
{
this.beatmap = beatmap;
}
protected override IBeatmap GetBeatmap() => beatmap;
protected override Texture GetBackground() => null;
protected override Track GetTrack() => null;
}
} }
} }

View File

@ -30,7 +30,7 @@ namespace osu.Game.Beatmaps
this.audioManager = audioManager; this.audioManager = audioManager;
} }
protected override Beatmap GetBeatmap() protected override IBeatmap GetBeatmap()
{ {
try try
{ {

View File

@ -2,30 +2,47 @@
// 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.Linq; using System.Linq;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
{ {
public interface IBeatmapProcessor
{
IBeatmap Beatmap { get; }
/// <summary>
/// Post-processes <see cref="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>
void PostProcess();
}
/// <summary> /// <summary>
/// Processes a post-converted Beatmap. /// Processes a post-converted Beatmap.
/// </summary> /// </summary>
/// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam> /// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam>
public class BeatmapProcessor<TObject> public class BeatmapProcessor : IBeatmapProcessor
where TObject : HitObject
{ {
public IBeatmap Beatmap { get; }
public BeatmapProcessor(IBeatmap beatmap)
{
Beatmap = beatmap;
}
/// <summary> /// <summary>
/// Post-processes a Beatmap to add mode-specific components that aren't added during conversion. /// Post-processes a Beatmap to add mode-specific components that aren't added during conversion.
/// <para> /// <para>
/// An example of such a usage is for combo colours. /// An example of such a usage is for combo colours.
/// </para> /// </para>
/// </summary> /// </summary>
/// <param name="beatmap">The Beatmap to process.</param> public virtual void PostProcess()
public virtual void PostProcess(Beatmap<TObject> beatmap)
{ {
IHasComboInformation lastObj = null; IHasComboInformation lastObj = null;
foreach (var obj in beatmap.HitObjects.OfType<IHasComboInformation>()) foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>())
{ {
if (obj.NewCombo) if (obj.NewCombo)
{ {

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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.Rulesets.Objects;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Framework.Timing; using osu.Framework.Timing;
@ -12,30 +11,17 @@ namespace osu.Game.Beatmaps
{ {
public abstract class DifficultyCalculator public abstract class DifficultyCalculator
{ {
protected double TimeRate = 1; protected readonly IBeatmap Beatmap;
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
}
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
{
protected readonly Beatmap<T> Beatmap;
protected readonly Mod[] Mods; protected readonly Mod[] Mods;
protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null) protected double TimeRate = 1;
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null)
{ {
Beatmap = beatmap;
Mods = mods ?? new Mod[0]; Mods = mods ?? new Mod[0];
var converter = CreateBeatmapConverter(beatmap);
foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<T>>())
mod.ApplyToBeatmapConverter(converter);
Beatmap = converter.Convert(beatmap);
ApplyMods(Mods); ApplyMods(Mods);
PreprocessHitObjects();
} }
protected virtual void ApplyMods(Mod[] mods) protected virtual void ApplyMods(Mod[] mods)
@ -43,22 +29,12 @@ namespace osu.Game.Beatmaps
var clock = new StopwatchClock(); var clock = new StopwatchClock();
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock)); mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
TimeRate = clock.Rate; TimeRate = clock.Rate;
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
foreach (var mod in mods.OfType<IApplicableToHitObject<T>>())
foreach (var obj in Beatmap.HitObjects)
mod.ApplyToHitObject(obj);
} }
protected virtual void PreprocessHitObjects() protected virtual void PreprocessHitObjects()
{ {
} }
protected abstract BeatmapConverter<T> CreateBeatmapConverter(Beatmap beatmap); public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
} }
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.Textures;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -39,7 +40,7 @@ namespace osu.Game.Beatmaps
this.game = game; this.game = game;
} }
protected override Beatmap GetBeatmap() => new Beatmap(); protected override IBeatmap GetBeatmap() => new Beatmap();
protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4"); protected override Texture GetBackground() => game.Textures.Get(@"Backgrounds/bg4");
@ -53,12 +54,14 @@ namespace osu.Game.Beatmaps
{ {
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { }; public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[] { };
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap)
{ {
throw new NotImplementedException(); throw new NotImplementedException();
} }
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null; public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => null;
public override string Description => "dummy"; public override string Description => "dummy";
@ -68,6 +71,14 @@ namespace osu.Game.Beatmaps
: base(rulesetInfo) : base(rulesetInfo)
{ {
} }
private class DummyBeatmapConverter : IBeatmapConverter
{
public event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
public IBeatmap Beatmap { get; set; }
public bool CanConvert => true;
public IBeatmap Convert() => Beatmap;
}
} }
} }
} }

View File

@ -373,17 +373,18 @@ namespace osu.Game.Beatmaps.Formats
if (parser == null) if (parser == null)
parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser(); parser = new Rulesets.Objects.Legacy.Osu.ConvertHitObjectParser();
var obj = parser.Parse(line); var obj = parser.Parse(line, getOffsetTime());
if (obj != null) if (obj != null)
{ {
obj.StartTime = getOffsetTime(obj.StartTime);
beatmap.HitObjects.Add(obj); beatmap.HitObjects.Add(obj);
} }
} }
private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0); private int getOffsetTime(int time) => time + (ApplyOffsets ? offset : 0);
private double getOffsetTime() => ApplyOffsets ? offset : 0;
private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0); private double getOffsetTime(double time) => time + (ApplyOffsets ? offset : 0);
} }
} }

View File

@ -0,0 +1,56 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Timing;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Beatmaps
{
public interface IBeatmap : IJsonSerializable
{
/// <summary>
/// This beatmap's info.
/// </summary>
BeatmapInfo BeatmapInfo { get; set; }
/// <summary>
/// This beatmap's metadata.
/// </summary>
BeatmapMetadata Metadata { get; }
/// <summary>
/// The control points in this beatmap.
/// </summary>
ControlPointInfo ControlPointInfo { get; }
/// <summary>
/// The breaks in this beatmap.
/// </summary>
List<BreakPeriod> Breaks { get; }
/// <summary>
/// Total amount of break time in the beatmap.
/// </summary>
double TotalBreakTime { get; }
/// <summary>
/// The hitobjects contained by this beatmap.
/// </summary>
IEnumerable<HitObject> HitObjects { get; }
/// <summary>
/// Returns statistics for the <see cref="HitObjects"/> contained in this beatmap.
/// </summary>
/// <returns></returns>
IEnumerable<BeatmapStatistic> GetStatistics();
/// <summary>
/// Creates a shallow-clone of this beatmap and returns it.
/// </summary>
/// <returns>The shallow-cloned beatmap.</returns>
IBeatmap Clone();
}
}

View File

@ -16,10 +16,16 @@ namespace osu.Game.Beatmaps
/// </summary> /// </summary>
event Action<HitObject, IEnumerable<HitObject>> ObjectConverted; event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
IBeatmap Beatmap { get; }
/// <summary> /// <summary>
/// Converts a Beatmap using this Beatmap Converter. /// Whether <see cref="Beatmap"/> can be converted by this <see cref="IBeatmapConverter"/>.
/// </summary> /// </summary>
/// <param name="beatmap">The un-converted Beatmap.</param> bool CanConvert { get; }
void Convert(Beatmap beatmap);
/// <summary>
/// Converts <see cref="Beatmap"/>.
/// </summary>
IBeatmap Convert();
} }
} }

View File

@ -1,21 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Beatmaps.Legacy
{
/// <summary>
/// A type of Beatmap loaded from a legacy .osu beatmap file (version &lt;=15).
/// </summary>
public class LegacyBeatmap : Beatmap
{
/// <summary>
/// Constructs a new beatmap.
/// </summary>
/// <param name="original">The original beatmap to use the parameters of.</param>
internal LegacyBeatmap(Beatmap original = null)
: base(original)
{
HitObjects = original?.HitObjects;
}
}
}

View File

@ -14,6 +14,8 @@ using osu.Framework.IO.File;
using System.IO; using System.IO;
using osu.Game.IO.Serialization; using osu.Game.IO.Serialization;
using System.Diagnostics; using System.Diagnostics;
using osu.Game.Rulesets;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
namespace osu.Game.Beatmaps namespace osu.Game.Beatmaps
@ -36,7 +38,7 @@ namespace osu.Game.Beatmaps
Mods.ValueChanged += mods => applyRateAdjustments(); Mods.ValueChanged += mods => applyRateAdjustments();
beatmap = new AsyncLazy<Beatmap>(populateBeatmap); beatmap = new AsyncLazy<IBeatmap>(populateBeatmap);
background = new AsyncLazy<Texture>(populateBackground, b => b == null || !b.IsDisposed); background = new AsyncLazy<Texture>(populateBackground, b => b == null || !b.IsDisposed);
track = new AsyncLazy<Track>(populateTrack); track = new AsyncLazy<Track>(populateTrack);
waveform = new AsyncLazy<Waveform>(populateWaveform); waveform = new AsyncLazy<Waveform>(populateWaveform);
@ -45,7 +47,7 @@ namespace osu.Game.Beatmaps
} }
/// <summary> /// <summary>
/// Saves the <see cref="Beatmap"/>. /// Saves the <see cref="Beatmaps.Beatmap"/>.
/// </summary> /// </summary>
public void Save() public void Save()
{ {
@ -55,7 +57,7 @@ namespace osu.Game.Beatmaps
Process.Start(path); Process.Start(path);
} }
protected abstract Beatmap GetBeatmap(); protected abstract IBeatmap GetBeatmap();
protected abstract Texture GetBackground(); protected abstract Texture GetBackground();
protected abstract Track GetTrack(); protected abstract Track GetTrack();
protected virtual Skin GetSkin() => new DefaultSkin(); protected virtual Skin GetSkin() => new DefaultSkin();
@ -63,12 +65,11 @@ namespace osu.Game.Beatmaps
protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo }; protected virtual Storyboard GetStoryboard() => new Storyboard { BeatmapInfo = BeatmapInfo };
public bool BeatmapLoaded => beatmap.IsResultAvailable; public bool BeatmapLoaded => beatmap.IsResultAvailable;
public Beatmap Beatmap => beatmap.Value.Result; public IBeatmap Beatmap => beatmap.Value.Result;
public async Task<Beatmap> GetBeatmapAsync() => await beatmap.Value; public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value;
private readonly AsyncLazy<IBeatmap> beatmap;
private readonly AsyncLazy<Beatmap> beatmap; private IBeatmap populateBeatmap()
private Beatmap populateBeatmap()
{ {
var b = GetBeatmap() ?? new Beatmap(); var b = GetBeatmap() ?? new Beatmap();
@ -78,6 +79,51 @@ namespace osu.Game.Beatmaps
return b; return b;
} }
/// <summary>
/// Constructs a playable <see cref="IBeatmap"/> from <see cref="Beatmap"/> using the applicable converters for a specific <see cref="RulesetInfo"/>.
/// <para>
/// The returned <see cref="IBeatmap"/> is in a playable state - all <see cref="HitObject"/> and <see cref="BeatmapDifficulty"/> <see cref="Mod"/>s
/// have been applied, and <see cref="HitObject"/>s have been fully constructed.
/// </para>
/// </summary>
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset)
{
var rulesetInstance = ruleset.CreateInstance();
IBeatmapConverter converter = rulesetInstance.CreateBeatmapConverter(Beatmap);
// Check if the beatmap can be converted
if (!converter.CanConvert)
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
// Apply conversion mods
foreach (var mod in Mods.Value.OfType<IApplicableToBeatmapConverter>())
mod.ApplyToBeatmapConverter(converter);
// Convert
IBeatmap converted = converter.Convert();
// Apply difficulty mods
foreach (var mod in Mods.Value.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
// Post-process
rulesetInstance.CreateBeatmapProcessor(converted)?.PostProcess();
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
foreach (var obj in converted.HitObjects)
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
foreach (var mod in Mods.Value.OfType<IApplicableToHitObject>())
foreach (var obj in converted.HitObjects)
mod.ApplyToHitObject(obj);
return converted;
}
public bool BackgroundLoaded => background.IsResultAvailable; public bool BackgroundLoaded => background.IsResultAvailable;
public Texture Background => background.Value.Result; public Texture Background => background.Value.Result;
public async Task<Texture> GetBackgroundAsync() => await background.Value; public async Task<Texture> GetBackgroundAsync() => await background.Value;

View File

@ -14,14 +14,18 @@ namespace osu.Game.Graphics.UserInterface
public class BreadcrumbControl<T> : OsuTabControl<T> public class BreadcrumbControl<T> : OsuTabControl<T>
{ {
private const float padding = 10; private const float padding = 10;
private const float item_chevron_size = 10;
protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value); protected override TabItem<T> CreateTabItem(T value) => new BreadcrumbTabItem(value)
{
AccentColour = AccentColour,
};
protected override float StripWidth() => base.StripWidth() - (padding + 8); protected override float StripWidth() => base.StripWidth() - (padding + item_chevron_size);
public BreadcrumbControl() public BreadcrumbControl()
{ {
Height = 26; Height = 32;
TabContainer.Spacing = new Vector2(padding, 0f); TabContainer.Spacing = new Vector2(padding, 0f);
Current.ValueChanged += tab => Current.ValueChanged += tab =>
{ {
@ -47,6 +51,7 @@ namespace osu.Game.Graphics.UserInterface
public override bool HandleKeyboardInput => State == Visibility.Visible; public override bool HandleKeyboardInput => State == Visibility.Visible;
public override bool HandleMouseInput => State == Visibility.Visible; public override bool HandleMouseInput => State == Visibility.Visible;
public override bool IsRemovable => true;
private Visibility state; private Visibility state;
@ -77,13 +82,14 @@ namespace osu.Game.Graphics.UserInterface
public BreadcrumbTabItem(T value) : base(value) public BreadcrumbTabItem(T value) : base(value)
{ {
Text.TextSize = 16; Text.TextSize = 18;
Padding = new MarginPadding { Right = padding + 8 }; //padding + chevron width Text.Margin = new MarginPadding { Vertical = 8 };
Padding = new MarginPadding { Right = padding + item_chevron_size };
Add(Chevron = new SpriteIcon Add(Chevron = new SpriteIcon
{ {
Anchor = Anchor.CentreRight, Anchor = Anchor.CentreRight,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Size = new Vector2(12), Size = new Vector2(item_chevron_size),
Icon = FontAwesome.fa_chevron_right, Icon = FontAwesome.fa_chevron_right,
Margin = new MarginPadding { Left = padding }, Margin = new MarginPadding { Left = padding },
Alpha = 0f, Alpha = 0f,

View File

@ -201,6 +201,8 @@ namespace osu.Game
return; return;
} }
Ruleset.Value = s.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
Beatmap.Value.Mods.Value = s.Mods; Beatmap.Value.Mods.Value = s.Mods;

View File

@ -12,10 +12,8 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -181,7 +179,7 @@ namespace osu.Game.Overlays
{ {
textbox.HoldFocus = false; textbox.HoldFocus = false;
if (1f - ChatHeight.Value < channel_selection_min_height) if (1f - ChatHeight.Value < channel_selection_min_height)
transformChatHeightTo(1f - channel_selection_min_height, 800, Easing.OutQuint); this.TransformBindableTo(ChatHeight, 1f - channel_selection_min_height, 800, Easing.OutQuint);
} }
else else
textbox.HoldFocus = true; textbox.HoldFocus = true;
@ -533,26 +531,5 @@ namespace osu.Game.Overlays
api.Queue(req); api.Queue(req);
} }
private void transformChatHeightTo(double newChatHeight, double duration = 0, Easing easing = Easing.None)
{
this.TransformTo(this.PopulateTransform(new TransformChatHeight(), newChatHeight, duration, easing));
}
private class TransformChatHeight : Transform<double, ChatOverlay>
{
private double valueAt(double time)
{
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
public override string TargetMember => "ChatHeight.Value";
protected override void Apply(ChatOverlay d, double time) => d.ChatHeight.Value = valueAt(time);
protected override void ReadIntoStartValue(ChatOverlay d) => StartValue = d.ChatHeight.Value;
}
} }
} }

View File

@ -1,8 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -12,6 +10,8 @@ using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
{ {
@ -22,6 +22,7 @@ namespace osu.Game.Overlays.Direct
protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552"); protected override Color4 BackgroundColour => OsuColour.FromHex(@"384552");
protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked; protected override DirectSortCriteria DefaultTab => DirectSortCriteria.Ranked;
protected override Drawable CreateSupplementaryControls() protected override Drawable CreateSupplementaryControls()
{ {
modeButtons = new FillFlowContainer<RulesetToggleButton> modeButtons = new FillFlowContainer<RulesetToggleButton>
@ -38,7 +39,7 @@ namespace osu.Game.Overlays.Direct
{ {
DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark; DisplayStyleControl.Dropdown.AccentColour = colours.BlueDark;
Ruleset.BindTo(game?.Ruleset ?? new Bindable<RulesetInfo> { Value = rulesets.GetRuleset(0) }); Ruleset.Value = game?.Ruleset.Value ?? rulesets.GetRuleset(0);
foreach (var r in rulesets.AvailableRulesets) foreach (var r in rulesets.AvailableRulesets)
{ {
modeButtons.Add(new RulesetToggleButton(Ruleset, r)); modeButtons.Add(new RulesetToggleButton(Ruleset, r));
@ -49,14 +50,15 @@ namespace osu.Game.Overlays.Direct
{ {
private Drawable icon private Drawable icon
{ {
get { return iconContainer.Icon; } get => iconContainer.Icon;
set { iconContainer.Icon = value; } set => iconContainer.Icon = value;
} }
private RulesetInfo ruleset; private RulesetInfo ruleset;
public RulesetInfo Ruleset public RulesetInfo Ruleset
{ {
get { return ruleset; } get => ruleset;
set set
{ {
ruleset = value; ruleset = value;
@ -73,6 +75,9 @@ namespace osu.Game.Overlays.Direct
iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100); iconContainer.FadeTo(Ruleset.ID == obj?.ID ? 1f : 0.5f, 100);
} }
public override bool HandleKeyboardInput => !bindable.Disabled && base.HandleKeyboardInput;
public override bool HandleMouseInput => !bindable.Disabled && base.HandleMouseInput;
public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset) public RulesetToggleButton(Bindable<RulesetInfo> bindable, RulesetInfo ruleset)
{ {
this.bindable = bindable; this.bindable = bindable;

View File

@ -230,6 +230,26 @@ namespace osu.Game.Overlays.KeyBinding
return true; return true;
} }
protected override bool OnJoystickPress(InputState state, Framework.Input.JoystickEventArgs args)
{
if (!HasFocus)
return false;
bindTarget.UpdateKeyCombination(KeyCombination.FromInputState(state));
finalise();
return true;
}
protected override bool OnJoystickRelease(InputState state, Framework.Input.JoystickEventArgs args)
{
if (!HasFocus)
return base.OnJoystickRelease(state, args);
finalise();
return true;
}
private void finalise() private void finalise()
{ {
if (bindTarget != null) if (bindTarget != null)

View File

@ -116,6 +116,7 @@ namespace osu.Game.Overlays.Mods
} }
private Mod mod; private Mod mod;
private readonly Container scaleContainer;
public Mod Mod public Mod Mod
{ {
@ -149,14 +150,26 @@ namespace osu.Game.Overlays.Mods
protected override bool OnMouseDown(InputState state, MouseDownEventArgs args) protected override bool OnMouseDown(InputState state, MouseDownEventArgs args)
{ {
switch (args.Button) scaleContainer.ScaleTo(0.9f, 800, Easing.Out);
return base.OnMouseDown(state, args);
}
protected override bool OnMouseUp(InputState state, MouseUpEventArgs args)
{
scaleContainer.ScaleTo(1, 500, Easing.OutElastic);
// only trigger the event if we are inside the area of the button
if (Contains(ToScreenSpace(state.Mouse.Position - Position)))
{ {
case MouseButton.Left: switch (args.Button)
SelectNext(1); {
break; case MouseButton.Left:
case MouseButton.Right: SelectNext(1);
SelectNext(-1); break;
break; case MouseButton.Right:
SelectNext(-1);
break;
}
} }
return true; return true;
@ -176,7 +189,8 @@ namespace osu.Game.Overlays.Mods
start = Mods.Length - 1; start = Mods.Length - 1;
for (int i = start; i < Mods.Length && i >= 0; i += direction) for (int i = start; i < Mods.Length && i >= 0; i += direction)
if (SelectAt(i)) return; if (SelectAt(i))
return;
Deselect(); Deselect();
} }
@ -242,8 +256,14 @@ namespace osu.Game.Overlays.Mods
Anchor = Anchor.TopCentre, Anchor = Anchor.TopCentre,
Children = new Drawable[] Children = new Drawable[]
{ {
iconsContainer = new Container<ModIcon> scaleContainer = new Container
{ {
Child = iconsContainer = new Container<ModIcon>
{
RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
},
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -87,8 +87,8 @@ namespace osu.Game.Overlays.Toolbar
ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault(); ruleset.Value = rulesets.AvailableRulesets.FirstOrDefault();
} }
public override bool HandleKeyboardInput => !ruleset.Disabled; public override bool HandleKeyboardInput => !ruleset.Disabled && base.HandleKeyboardInput;
public override bool HandleMouseInput => !ruleset.Disabled; public override bool HandleMouseInput => !ruleset.Disabled && base.HandleMouseInput;
private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300); private void disabledChanged(bool isDisabled) => this.FadeColour(isDisabled ? Color4.Gray : Color4.White, 300);

View File

@ -125,7 +125,7 @@ namespace osu.Game.Rulesets.Edit
private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool; private void setCompositionTool(ICompositionTool tool) => CurrentTool = tool;
protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap, true); protected virtual RulesetContainer CreateRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap) => ruleset.CreateRulesetContainerWith(beatmap);
protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; } protected abstract IReadOnlyList<ICompositionTool> CompositionTools { get; }

View File

@ -10,13 +10,12 @@ namespace osu.Game.Rulesets.Mods
/// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>. /// Interface for a <see cref="Mod"/> that applies changes to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary> /// </summary>
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam> /// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
public interface IApplicableToBeatmapConverter<TObject> : IApplicableMod public interface IApplicableToBeatmapConverter : IApplicableMod
where TObject : HitObject
{ {
/// <summary> /// <summary>
/// Applies this <see cref="Mod"/> to a <see cref="BeatmapConverter{TObject}"/>. /// Applies this <see cref="Mod"/> to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary> /// </summary>
/// <param name="beatmapConverter">The <see cref="BeatmapConverter{TObject}"/> to apply to.</param> /// <param name="beatmapConverter">The <see cref="BeatmapConverter{TObject}"/> to apply to.</param>
void ApplyToBeatmapConverter(BeatmapConverter<TObject> beatmapConverter); void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter);
} }
} }

View File

@ -8,13 +8,12 @@ namespace osu.Game.Rulesets.Mods
/// <summary> /// <summary>
/// An interface for <see cref="Mod"/>s that can be applied to <see cref="HitObject"/>s. /// An interface for <see cref="Mod"/>s that can be applied to <see cref="HitObject"/>s.
/// </summary> /// </summary>
public interface IApplicableToHitObject<in TObject> : IApplicableMod public interface IApplicableToHitObject : IApplicableMod
where TObject : HitObject
{ {
/// <summary> /// <summary>
/// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>. /// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>.
/// </summary> /// </summary>
/// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param> /// <param name="hitObject">The <see cref="HitObject"/> to apply to.</param>
void ApplyToHitObject(TObject hitObject); void ApplyToHitObject(HitObject hitObject);
} }
} }

View File

@ -19,6 +19,11 @@ namespace osu.Game.Rulesets.Objects.Legacy
public abstract class ConvertHitObjectParser : HitObjectParser public abstract class ConvertHitObjectParser : HitObjectParser
{ {
public override HitObject Parse(string text) public override HitObject Parse(string text)
{
return Parse(text, 0);
}
public HitObject Parse(string text, double offset)
{ {
try try
{ {
@ -146,7 +151,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
} }
else if ((type & ConvertHitObjectType.Spinner) > 0) else if ((type & ConvertHitObjectType.Spinner) > 0)
{ {
result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture)); result = CreateSpinner(new Vector2(512, 384) / 2, Convert.ToDouble(split[5], CultureInfo.InvariantCulture) + offset);
if (split.Length > 6) if (split.Length > 6)
readCustomSampleBanks(split[6], bankInfo); readCustomSampleBanks(split[6], bankInfo);
@ -164,13 +169,13 @@ namespace osu.Game.Rulesets.Objects.Legacy
readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo); readCustomSampleBanks(string.Join(":", ss.Skip(1)), bankInfo);
} }
result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime); result = CreateHold(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, endTime + offset);
} }
if (result == null) if (result == null)
throw new InvalidOperationException($@"Unknown hit object type {type}."); throw new InvalidOperationException($@"Unknown hit object type {type}.");
result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture); result.StartTime = Convert.ToDouble(split[2], CultureInfo.InvariantCulture) + offset;
result.Samples = convertSoundType(soundType, bankInfo); result.Samples = convertSoundType(soundType, bankInfo);
return result; return result;

View File

@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Replays.Types
/// </summary> /// </summary>
/// <param name="legacyFrame">The <see cref="LegacyReplayFrame"/> to extract values from.</param> /// <param name="legacyFrame">The <see cref="LegacyReplayFrame"/> to extract values from.</param>
/// <param name="beatmap">The beatmap.</param> /// <param name="beatmap">The beatmap.</param>
void ConvertFrom(LegacyReplayFrame legacyFrame, Beatmap beatmap); void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap);
} }
} }

View File

@ -22,8 +22,6 @@ namespace osu.Game.Rulesets
{ {
public readonly RulesetInfo RulesetInfo; public readonly RulesetInfo RulesetInfo;
public virtual IEnumerable<BeatmapStatistic> GetBeatmapStatistics(WorkingBeatmap beatmap) => new BeatmapStatistic[] { };
public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>() public IEnumerable<Mod> GetAllMods() => Enum.GetValues(typeof(ModType)).Cast<ModType>()
// Confine all mods of each mod type into a single IEnumerable<Mod> // Confine all mods of each mod type into a single IEnumerable<Mod>
.SelectMany(GetModsFor) .SelectMany(GetModsFor)
@ -52,14 +50,17 @@ namespace osu.Game.Rulesets
/// Attempt to create a hit renderer for a beatmap /// Attempt to create a hit renderer for a beatmap
/// </summary> /// </summary>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param> /// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether the hit renderer should assume the beatmap is for the current ruleset.</param>
/// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception> /// <exception cref="BeatmapInvalidForRulesetException">Unable to successfully load the beatmap to be usable with this ruleset.</exception>
/// <returns></returns> /// <returns></returns>
public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset); public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap);
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null); public abstract IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap);
public virtual PerformanceCalculator CreatePerformanceCalculator(Beatmap beatmap, Score score) => null; public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null;
public abstract DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null);
public virtual PerformanceCalculator CreatePerformanceCalculator(IBeatmap beatmap, Score score) => null;
public virtual HitObjectComposer CreateHitObjectComposer() => null; public virtual HitObjectComposer CreateHitObjectComposer() => null;
@ -114,7 +115,8 @@ namespace osu.Game.Rulesets
Name = Description, Name = Description,
ShortName = ShortName, ShortName = ShortName,
InstantiationInfo = GetType().AssemblyQualifiedName, InstantiationInfo = GetType().AssemblyQualifiedName,
ID = LegacyID ID = LegacyID,
Available = true
}; };
} }
} }

View File

@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
} }
private Beatmap currentBeatmap; private IBeatmap currentBeatmap;
private Ruleset currentRuleset; private Ruleset currentRuleset;
public Score Parse(Stream stream) public Score Parse(Stream stream)
@ -49,18 +49,21 @@ namespace osu.Game.Rulesets.Scoring.Legacy
score.User = new User { Username = sr.ReadString() }; score.User = new User { Username = sr.ReadString() };
/* var localScoreChecksum = */ /* var localScoreChecksum = */
sr.ReadString(); sr.ReadString();
/* score.Count300 = */
sr.ReadUInt16(); var count300 = sr.ReadUInt16();
/* score.Count100 = */ var count100 = sr.ReadUInt16();
sr.ReadUInt16(); var count50 = sr.ReadUInt16();
/* score.Count50 = */ var countGeki = sr.ReadUInt16();
sr.ReadUInt16(); var countKatu = sr.ReadUInt16();
/* score.CountGeki = */ var countMiss = sr.ReadUInt16();
sr.ReadUInt16();
/* score.CountKatu = */ score.Statistics[HitResult.Great] = count300;
sr.ReadUInt16(); score.Statistics[HitResult.Good] = count100;
/* score.CountMiss = */ score.Statistics[HitResult.Meh] = count50;
sr.ReadUInt16(); score.Statistics[HitResult.Perfect] = countGeki;
score.Statistics[HitResult.Ok] = countKatu;
score.Statistics[HitResult.Miss] = countMiss;
score.TotalScore = sr.ReadInt32(); score.TotalScore = sr.ReadInt32();
score.MaxCombo = sr.ReadUInt16(); score.MaxCombo = sr.ReadUInt16();
/* score.Perfect = */ /* score.Perfect = */
@ -81,6 +84,34 @@ namespace osu.Game.Rulesets.Scoring.Legacy
/*OnlineId =*/ /*OnlineId =*/
sr.ReadInt32(); sr.ReadInt32();
switch (score.Ruleset.ID)
{
case 0:
{
int totalHits = count50 + count100 + count300 + countMiss;
score.Accuracy = totalHits > 0 ? (double)(count50 * 50 + count100 * 100 + count300 * 300) / (totalHits * 300) : 1;
break;
}
case 1:
{
int totalHits = count50 + count100 + count300 + countMiss;
score.Accuracy = totalHits > 0 ? (double)(count100 * 150 + count300 * 300) / (totalHits * 300) : 1;
break;
}
case 2:
{
int totalHits = count50 + count100 + count300 + countMiss + countKatu;
score.Accuracy = totalHits > 0 ? (double)(count50 + count100 + count300 ) / totalHits : 1;
break;
}
case 3:
{
int totalHits = count50 + count100 + count300 + countMiss + countGeki + countKatu;
score.Accuracy = totalHits > 0 ? (double)(count50 * 50 + count100 * 100 + countKatu * 200 + (count300 + countGeki) * 300) / (totalHits * 300) : 1;
break;
}
}
using (var replayInStream = new MemoryStream(compressedReplay)) using (var replayInStream = new MemoryStream(compressedReplay))
{ {
byte[] properties = new byte[5]; byte[] properties = new byte[5];

View File

@ -2,42 +2,28 @@
// 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 System.Linq;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Scoring namespace osu.Game.Rulesets.Scoring
{ {
public abstract class PerformanceCalculator public abstract class PerformanceCalculator
{
public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
}
public abstract class PerformanceCalculator<TObject> : PerformanceCalculator
where TObject : HitObject
{ {
private readonly Dictionary<string, double> attributes = new Dictionary<string, double>(); private readonly Dictionary<string, double> attributes = new Dictionary<string, double>();
protected IDictionary<string, double> Attributes => attributes; protected IDictionary<string, double> Attributes => attributes;
protected readonly Beatmap<TObject> Beatmap; protected readonly IBeatmap Beatmap;
protected readonly Score Score; protected readonly Score Score;
protected PerformanceCalculator(Ruleset ruleset, Beatmap beatmap, Score score) protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
{ {
Score = score; Score = score;
var converter = CreateBeatmapConverter(); Beatmap = beatmap;
foreach (var mod in score.Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
mod.ApplyToBeatmapConverter(converter);
Beatmap = converter.Convert(beatmap);
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods); var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
diffCalc.Calculate(attributes); diffCalc.Calculate(attributes);
} }
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter(); public abstract double Calculate(Dictionary<string, double> categoryDifficulty = null);
} }
} }

View File

@ -190,11 +190,6 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
protected readonly WorkingBeatmap WorkingBeatmap; protected readonly WorkingBeatmap WorkingBeatmap;
/// <summary>
/// Whether the specified beatmap is assumed to be specific to the current ruleset.
/// </summary>
public readonly bool IsForCurrentRuleset;
public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this); public override ScoreProcessor CreateScoreProcessor() => new ScoreProcessor<TObject>(this);
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
@ -206,43 +201,18 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
/// <param name="ruleset">The ruleset being repesented.</param> /// <param name="ruleset">The ruleset being repesented.</param>
/// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param> /// <param name="workingBeatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param> protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap)
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap, bool isForCurrentRuleset)
: base(ruleset) : base(ruleset)
{ {
Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap."); Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
WorkingBeatmap = workingBeatmap; WorkingBeatmap = workingBeatmap;
IsForCurrentRuleset = isForCurrentRuleset;
// ReSharper disable once PossibleNullReferenceException // ReSharper disable once PossibleNullReferenceException
Mods = workingBeatmap.Mods.Value; Mods = workingBeatmap.Mods.Value;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
BeatmapConverter<TObject> converter = CreateBeatmapConverter(); Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
BeatmapProcessor<TObject> processor = CreateBeatmapProcessor();
// Check if the beatmap can be converted
if (!converter.CanConvert(workingBeatmap.Beatmap))
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmap)} can not be converted for the current ruleset (converter: {converter}).");
// Apply conversion adjustments before converting
foreach (var mod in Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
mod.ApplyToBeatmapConverter(converter);
// Convert the beatmap
Beatmap = converter.Convert(workingBeatmap.Beatmap);
// Apply difficulty adjustments from mods before using Difficulty.
foreach (var mod in Mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(Beatmap.BeatmapInfo.BaseDifficulty);
// Post-process the beatmap
processor.PostProcess(Beatmap);
// Apply defaults
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
KeyBindingInputManager = CreateInputManager(); KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both; KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
@ -277,10 +247,6 @@ namespace osu.Game.Rulesets.UI
if (mods == null) if (mods == null)
return; return;
foreach (var mod in mods.OfType<IApplicableToHitObject<TObject>>())
foreach (var obj in Beatmap.HitObjects)
mod.ApplyToHitObject(obj);
foreach (var mod in mods.OfType<IApplicableToRulesetContainer<TObject>>()) foreach (var mod in mods.OfType<IApplicableToRulesetContainer<TObject>>())
mod.ApplyToRulesetContainer(this); mod.ApplyToRulesetContainer(this);
} }
@ -324,13 +290,6 @@ namespace osu.Game.Rulesets.UI
Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea; Playfield.Size = GetAspectAdjustedSize() * PlayfieldArea;
} }
/// <summary>
/// Creates a processor to perform post-processing operations
/// on HitObjects in converted Beatmaps.
/// </summary>
/// <returns>The Beatmap processor.</returns>
protected virtual BeatmapProcessor<TObject> CreateBeatmapProcessor() => new BeatmapProcessor<TObject>();
/// <summary> /// <summary>
/// Computes the size of the <see cref="Playfield"/> in relative coordinate space after aspect adjustments. /// Computes the size of the <see cref="Playfield"/> in relative coordinate space after aspect adjustments.
/// </summary> /// </summary>
@ -344,12 +303,6 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default protected virtual Vector2 PlayfieldArea => new Vector2(0.75f); // A sane default
/// <summary>
/// Creates a converter to convert Beatmap to a specific mode.
/// </summary>
/// <returns>The Beatmap converter.</returns>
protected abstract BeatmapConverter<TObject> CreateBeatmapConverter();
/// <summary> /// <summary>
/// Creates a DrawableHitObject from a HitObject. /// Creates a DrawableHitObject from a HitObject.
/// </summary> /// </summary>
@ -377,9 +330,8 @@ namespace osu.Game.Rulesets.UI
/// </summary> /// </summary>
/// <param name="ruleset">The ruleset being repesented.</param> /// <param name="ruleset">The ruleset being repesented.</param>
/// <param name="beatmap">The beatmap to create the hit renderer for.</param> /// <param name="beatmap">The beatmap to create the hit renderer for.</param>
/// <param name="isForCurrentRuleset">Whether to assume the beatmap is for the current ruleset.</param> protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) : base(ruleset, beatmap)
: base(ruleset, beatmap, isForCurrentRuleset)
{ {
} }
} }

View File

@ -4,9 +4,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.MathUtils;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using OpenTK.Input; using OpenTK.Input;
@ -90,10 +88,10 @@ namespace osu.Game.Rulesets.UI.Scrolling
switch (args.Key) switch (args.Key)
{ {
case Key.Minus: case Key.Minus:
transformVisibleTimeRangeTo(VisibleTimeRange + time_span_step, 200, Easing.OutQuint); this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange + time_span_step, 200, Easing.OutQuint);
break; break;
case Key.Plus: case Key.Plus:
transformVisibleTimeRangeTo(VisibleTimeRange - time_span_step, 200, Easing.OutQuint); this.TransformBindableTo(VisibleTimeRange, VisibleTimeRange - time_span_step, 200, Easing.OutQuint);
break; break;
} }
} }
@ -101,27 +99,6 @@ namespace osu.Game.Rulesets.UI.Scrolling
return false; return false;
} }
private void transformVisibleTimeRangeTo(double newTimeRange, double duration = 0, Easing easing = Easing.None)
{
this.TransformTo(this.PopulateTransform(new TransformVisibleTimeRange(), newTimeRange, duration, easing));
}
protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction); protected sealed override HitObjectContainer CreateHitObjectContainer() => new ScrollingHitObjectContainer(direction);
private class TransformVisibleTimeRange : Transform<double, ScrollingPlayfield>
{
private double valueAt(double time)
{
if (time < StartTime) return StartValue;
if (time >= EndTime) return EndValue;
return Interpolation.ValueAt(time, StartValue, EndValue, StartTime, EndTime, Easing);
}
public override string TargetMember => "VisibleTimeRange.Value";
protected override void Apply(ScrollingPlayfield d, double time) => d.VisibleTimeRange.Value = valueAt(time);
protected override void ReadIntoStartValue(ScrollingPlayfield d) => StartValue = d.VisibleTimeRange.Value;
}
} }
} }

View File

@ -30,8 +30,8 @@ namespace osu.Game.Rulesets.UI.Scrolling
/// <returns></returns> /// <returns></returns>
protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default); protected readonly SortedList<MultiplierControlPoint> DefaultControlPoints = new SortedList<MultiplierControlPoint>(Comparer<MultiplierControlPoint>.Default);
protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset) protected ScrollingRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap, isForCurrentRuleset) : base(ruleset, beatmap)
{ {
} }

View File

@ -27,6 +27,7 @@ namespace osu.Game.Screens.Edit
protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4"); protected override BackgroundScreen CreateBackground() => new BackgroundScreenCustom(@"Backgrounds/bg4");
public override bool ShowOverlaysOnEnter => false; public override bool ShowOverlaysOnEnter => false;
public override bool AllowBeatmapRulesetChange => false;
private Box bottomBackground; private Box bottomBackground;
private Container screenContainer; private Container screenContainer;

View File

@ -14,7 +14,7 @@ using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Charts; using osu.Game.Screens.Charts;
using osu.Game.Screens.Direct; using osu.Game.Screens.Direct;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Multiplayer; using osu.Game.Screens.Multi.Screens;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Game.Screens.Tournament; using osu.Game.Screens.Tournament;

View File

@ -9,7 +9,7 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Components
{ {
public class DrawableGameType : CircularContainer, IHasTooltip public class DrawableGameType : CircularContainer, IHasTooltip
{ {

View File

@ -1,8 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -17,8 +15,10 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Users; using osu.Game.Users;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Components
{ {
public class DrawableRoom : OsuClickableContainer public class DrawableRoom : OsuClickableContainer
{ {

View File

@ -1,14 +1,14 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 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.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.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using OpenTK;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Components
{ {
public class ModeTypeInfo : Container public class ModeTypeInfo : Container
{ {

View File

@ -3,7 +3,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -11,8 +10,9 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Users; using osu.Game.Users;
using OpenTK;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Components
{ {
public class ParticipantInfo : Container public class ParticipantInfo : Container
{ {

View File

@ -2,8 +2,6 @@
// 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.Linq; using System.Linq;
using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -20,8 +18,10 @@ using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Users; using osu.Game.Users;
using OpenTK;
using OpenTK.Graphics;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Components
{ {
public class RoomInspector : Container public class RoomInspector : Container
{ {

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Screens
{ {
public class Lobby : ScreenWhiteBox public class Lobby : ScreenWhiteBox
{ {

View File

@ -3,14 +3,14 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using OpenTK.Graphics;
using osu.Game.Screens.Select; using osu.Game.Screens.Select;
using osu.Framework.Graphics; using OpenTK.Graphics;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Screens
{ {
public class Match : ScreenWhiteBox public class Match : ScreenWhiteBox
{ {

View File

@ -4,7 +4,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace osu.Game.Screens.Multiplayer namespace osu.Game.Screens.Multi.Screens
{ {
public class MatchCreate : ScreenWhiteBox public class MatchCreate : ScreenWhiteBox
{ {

View File

@ -93,7 +93,7 @@ namespace osu.Game.Screens.Play
mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel); mouseWheelDisabled = config.GetBindable<bool>(OsuSetting.MouseDisableWheel);
userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset); userAudioOffset = config.GetBindable<double>(OsuSetting.AudioOffset);
Beatmap beatmap; IBeatmap beatmap;
try try
{ {
@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play
try try
{ {
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID); RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working);
} }
catch (BeatmapInvalidForRulesetException) catch (BeatmapInvalidForRulesetException)
{ {
@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play
// let's try again forcing the beatmap's ruleset. // let's try again forcing the beatmap's ruleset.
ruleset = beatmap.BeatmapInfo.Ruleset; ruleset = beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance(); rulesetInstance = ruleset.CreateInstance();
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true); RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap);
} }
if (!RulesetContainer.Objects.Any()) if (!RulesetContainer.Objects.Any())

Some files were not shown because too many files have changed in this diff Show More