1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-19 03:03:21 +08:00

Merge branch 'master' into master

This commit is contained in:
Jacob Odgård Tørring 2018-05-10 17:55:37 +02:00 committed by GitHub
commit 7de502b20b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 303 additions and 255 deletions

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(IBeatmap 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

@ -13,6 +13,11 @@ 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, IBeatmap beatmap) protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)

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(IBeatmap 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(IBeatmap 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[]
{ {

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

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

@ -33,18 +33,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
private ManiaBeatmap beatmap; private ManiaBeatmap beatmap;
public ManiaBeatmapConverter(bool isForCurrentRuleset, IBeatmap 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)
@ -58,6 +59,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap 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);

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;
@ -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(IBeatmap 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)
{ {

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

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Replays
public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap 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(IBeatmap 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

@ -14,6 +14,11 @@ 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, IBeatmap beatmap) protected override IEnumerable<OsuHitObject> ConvertHitObject(HitObject original, IBeatmap beatmap)

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,14 +5,13 @@ 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;
@ -27,14 +26,9 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
{ {
} }
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(IBeatmap beatmap) => new OsuBeatmapConverter();
} }
} }

View File

@ -22,12 +22,15 @@ 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[]
{ {

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;
@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
{ {
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; beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count) + 1;
} }
@ -193,7 +192,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(IBeatmap 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

@ -42,9 +42,10 @@ 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(IBeatmap original) protected override Beatmap<TaikoHitObject> ConvertBeatmap(IBeatmap original)

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;
@ -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(IBeatmap 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[]
{ {

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

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

@ -22,25 +22,27 @@ 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(IBeatmap 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(IBeatmap original)
{ {
// We always operate on a clone of the original beatmap, to not modify it game-wide Beatmap = beatmap;
return ConvertBeatmap(original.Clone());
} }
void IBeatmapConverter.Convert(IBeatmap 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.

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

@ -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 double TimeRate = 1;
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) 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(IBeatmap 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
@ -53,11 +54,13 @@ 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 IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new DummyBeatmapConverter { Beatmap = beatmap };
public override DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null) => null; 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

@ -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(IBeatmap beatmap);
/// <summary>
/// Converts <see cref="Beatmap"/>.
/// </summary>
IBeatmap Convert();
} }
} }

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
@ -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()
{ {
@ -65,7 +67,6 @@ namespace osu.Game.Beatmaps
public bool BeatmapLoaded => beatmap.IsResultAvailable; public bool BeatmapLoaded => beatmap.IsResultAvailable;
public IBeatmap Beatmap => beatmap.Value.Result; public IBeatmap Beatmap => beatmap.Value.Result;
public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value; public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value;
private readonly AsyncLazy<IBeatmap> beatmap; private readonly AsyncLazy<IBeatmap> beatmap;
private IBeatmap populateBeatmap() private IBeatmap populateBeatmap()
@ -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

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

@ -52,10 +52,13 @@ 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 IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap);
public virtual IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => null;
public abstract DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null); public abstract DifficultyCalculator CreateDifficultyCalculator(IBeatmap beatmap, Mod[] mods = null);
@ -114,7 +117,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

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

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

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

View File

@ -10,12 +10,14 @@ using NUnit.Framework;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
namespace osu.Game.Tests.Beatmaps namespace osu.Game.Tests.Beatmaps
{ {
[TestFixture] [TestFixture]
public abstract class BeatmapConversionTest<TConvertValue> public abstract class BeatmapConversionTest<TRuleset, TConvertValue>
where TRuleset : Ruleset, new()
where TConvertValue : IEquatable<TConvertValue> where TConvertValue : IEquatable<TConvertValue>
{ {
private const string resource_namespace = "Testing.Beatmaps"; private const string resource_namespace = "Testing.Beatmaps";
@ -79,6 +81,9 @@ namespace osu.Game.Tests.Beatmaps
{ {
var beatmap = getBeatmap(name); var beatmap = getBeatmap(name);
var rulesetInstance = new TRuleset();
beatmap.BeatmapInfo.Ruleset = beatmap.BeatmapInfo.RulesetID == rulesetInstance.RulesetInfo.ID ? rulesetInstance.RulesetInfo : new RulesetInfo();
var result = new ConvertResult(); var result = new ConvertResult();
var converter = CreateConverter(beatmap); var converter = CreateConverter(beatmap);
@ -92,7 +97,7 @@ namespace osu.Game.Tests.Beatmaps
result.Mappings.Add(mapping); result.Mappings.Add(mapping);
}; };
converter.Convert(beatmap); converter.Convert();
return result; return result;
} }