1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 12:57:36 +08:00

Merge pull request #2491 from smoogipoo/beatmap-conversion-rework

Rework beatmap conversion process to use IBeatmap
This commit is contained in:
Dean Herbert 2018-05-10 20:08:37 +08:00 committed by GitHub
commit 0afe33d32f
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
{
public class CatchBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
public class CatchBeatmapConversionTest : BeatmapConversionTest<TestCatchRuleset, ConvertValue>
{
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>
@ -64,4 +64,8 @@ namespace osu.Game.Rulesets.Catch.Tests
=> Precision.AlmostEquals(StartTime, other.StartTime, 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 CatchBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasXPosition) };
protected override IEnumerable<CatchHitObject> ConvertHitObject(HitObject obj, IBeatmap beatmap)

View File

@ -12,16 +12,21 @@ using OpenTK;
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;
foreach (var obj in beatmap.HitObjects)
foreach (var obj in Beatmap.HitObjects.OfType<CatchHitObject>())
obj.IndexInBeatmap = index++;
}

View File

@ -2,20 +2,16 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using System.Collections.Generic;
namespace osu.Game.Rulesets.Catch
{
public class CatchDifficultyCalculator : DifficultyCalculator<CatchHitObject>
public class CatchDifficultyCalculator : DifficultyCalculator
{
public CatchDifficultyCalculator(IBeatmap beatmap) : base(beatmap)
{
}
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.Replays.Types;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Catch.Beatmaps;
namespace osu.Game.Rulesets.Catch
{
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[]
{

View File

@ -6,10 +6,11 @@ using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
using System;
using osu.Game.Rulesets.Objects;
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 bool Ranked => true;
@ -17,9 +18,11 @@ namespace osu.Game.Rulesets.Catch.Mods
private float lastStartX;
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;
if (lastStartX == 0)
@ -60,7 +63,7 @@ namespace osu.Game.Rulesets.Catch.Mods
position += rand;
}
hitObject.X = position;
catchObject.X = position;
return;
}
@ -79,7 +82,7 @@ namespace osu.Game.Rulesets.Catch.Mods
}
}
hitObject.X = position;
catchObject.X = position;
lastStartX = position;
lastStartTime = startTime;

View File

@ -4,7 +4,6 @@
using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Input.Handlers;
using osu.Game.Rulesets.Catch.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawable;
using osu.Game.Rulesets.Catch.Replays;
@ -20,8 +19,8 @@ namespace osu.Game.Rulesets.Catch.UI
{
public class CatchRulesetContainer : ScrollingRulesetContainer<CatchPlayfield, CatchHitObject>
{
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
public CatchRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
@ -29,10 +28,6 @@ namespace osu.Game.Rulesets.Catch.UI
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);
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
{
public class ManiaBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
public class ManiaBeatmapConversionTest : BeatmapConversionTest<TestManiaRuleset, ConvertValue>
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Mania";
private bool isForCurrentRuleset;
[NonParallelizable]
[TestCase("basic", false)]
public void Test(string name, bool isForCurrentRuleset)
[TestCase("basic")]
public new void Test(string name)
{
this.isForCurrentRuleset = isForCurrentRuleset;
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>
@ -57,4 +54,8 @@ namespace osu.Game.Rulesets.Mania.Tests
&& Precision.AlmostEquals(EndTime, other.EndTime, conversion_lenience)
&& Column == other.Column;
}
public class TestManiaRuleset : ManiaRuleset
{
}
}

View File

@ -33,18 +33,19 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
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 roundedOverallDifficulty = Math.Round(original.BeatmapInfo.BaseDifficulty.OverallDifficulty);
var roundedCircleSize = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize);
var roundedOverallDifficulty = Math.Round(beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty);
if (isForCurrentRuleset)
if (IsForCurrentRuleset)
TargetColumns = (int)Math.Max(1, roundedCircleSize);
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)
TargetColumns = 7;
else if (percentSliderOrSpinner < 0.3 || roundedCircleSize >= 5)
@ -58,6 +59,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
protected override Beatmap<ManiaHitObject> ConvertBeatmap(IBeatmap original)
{
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);

View File

@ -10,7 +10,7 @@ using System.Collections.Generic;
namespace osu.Game.Rulesets.Mania
{
internal class ManiaDifficultyCalculator : DifficultyCalculator<ManiaHitObject>
internal class ManiaDifficultyCalculator : DifficultyCalculator
{
private const double star_scaling_factor = 0.018;
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
int columnCount = (Beatmap as ManiaBeatmap)?.TotalColumns ?? 7;
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.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
@ -140,7 +140,5 @@ namespace osu.Game.Rulesets.Mania
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.Replays.Types;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Mania.Beatmaps;
namespace osu.Game.Rulesets.Mania
{
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)
{

View File

@ -3,19 +3,18 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.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 abstract int KeyCount { get; }
public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier
public override bool Ranked => true;
public void ApplyToBeatmapConverter(BeatmapConverter<ManiaHitObject> beatmapConverter)
public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter)
{
var mbc = (ManiaBeatmapConverter)beatmapConverter;

View File

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

View File

@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Mania.Replays
public void ConvertFrom(LegacyReplayFrame legacyFrame, IBeatmap beatmap)
{
// 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
// 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 ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
public ManiaRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
// Generate the bar lines
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);
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(IsForCurrentRuleset, WorkingBeatmap.Beatmap);
protected override DrawableHitObject<ManiaHitObject> GetVisualRepresentation(ManiaHitObject h)
{
ManiaAction action = Playfield.Columns.ElementAt(h.Column).Action;

View File

@ -15,7 +15,7 @@ using OpenTK;
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";
@ -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>
@ -67,4 +67,8 @@ namespace osu.Game.Rulesets.Osu.Tests
&& Precision.AlmostEquals(EndX, other.EndX, 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>
{
public OsuBeatmapConverter(IBeatmap beatmap)
: base(beatmap)
{
}
protected override IEnumerable<Type> ValidConversionTypes { get; } = new[] { typeof(IHasPosition) };
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
{
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)

View File

@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Osu.Edit
{
public class OsuEditRulesetContainer : OsuRulesetContainer
{
public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
public OsuEditRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: 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[]
{

View File

@ -5,20 +5,23 @@ using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.UI;
using OpenTK;
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 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;
if (slider == null)

View File

@ -5,14 +5,13 @@ using System;
using System.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.OsuDifficulty.Preprocessing;
using osu.Game.Rulesets.Osu.OsuDifficulty.Skills;
namespace osu.Game.Rulesets.Osu.OsuDifficulty
{
public class OsuDifficultyCalculator : DifficultyCalculator<OsuHitObject>
public class OsuDifficultyCalculator : DifficultyCalculator
{
private const int section_length = 400;
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)
{
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects, TimeRate);
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap((List<OsuHitObject>)Beatmap.HitObjects, TimeRate);
Skill[] skills =
{
new Aim(),
@ -72,7 +66,5 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
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.Replays.Types;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Osu.Beatmaps;
namespace osu.Game.Rulesets.Osu
{
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[]
{

View File

@ -6,14 +6,13 @@ using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Scoring
{
public class OsuPerformanceCalculator : PerformanceCalculator<OsuHitObject>
public class OsuPerformanceCalculator : PerformanceCalculator
{
private readonly int countHitCircles;
private readonly int beatmapMaxCombo;
@ -32,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
{
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;
}
@ -193,7 +192,5 @@ namespace osu.Game.Rulesets.Osu.Scoring
private double totalHits => count300 + count100 + count50 + countMiss;
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.Input.Handlers;
using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Osu.Replays;
@ -21,17 +20,13 @@ namespace osu.Game.Rulesets.Osu.UI
{
public class OsuRulesetContainer : RulesetContainer<OsuHitObject>
{
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
public OsuRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
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();
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
{
public class TaikoBeatmapConversionTest : BeatmapConversionTest<ConvertValue>
public class TaikoBeatmapConversionTest : BeatmapConversionTest<TestTaikoRuleset, ConvertValue>
{
protected override string ResourceAssembly => "osu.Game.Rulesets.Taiko";
private bool isForCurrentRuleset;
[NonParallelizable]
[TestCase("basic", false), Ignore("See: https://github.com/ppy/osu/issues/2152")]
[TestCase("slider-generating-drumroll", false)]
public void Test(string name, bool isForCurrentRuleset)
public new void Test(string name)
{
this.isForCurrentRuleset = isForCurrentRuleset;
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>
@ -70,4 +67,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
&& IsSwell == other.IsSwell
&& IsStrong == other.IsStrong;
}
public class TestTaikoRuleset : TaikoRuleset
{
}
}

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
private Container playfieldContainer;
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
private void load()
{
AddStep("Hit!", () => addHitJudgement(false));
AddStep("Kiai hit", () => addHitJudgement(true));
@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
Title = @"Sample Beatmap",
AuthorString = @"peppy",
},
Ruleset = new TaikoRuleset().RulesetInfo
},
ControlPointInfo = controlPointInfo
});
@ -86,7 +87,7 @@ namespace osu.Game.Rulesets.Taiko.Tests
RelativeSizeAxes = Axes.X,
Height = 768,
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) };
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)

View File

@ -2,14 +2,13 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
using System.Collections.Generic;
using System;
namespace osu.Game.Rulesets.Taiko
{
internal class TaikoDifficultyCalculator : DifficultyCalculator<TaikoHitObject>
internal class TaikoDifficultyCalculator : DifficultyCalculator
{
private const double star_scaling_factor = 0.04125;
@ -41,7 +40,7 @@ namespace osu.Game.Rulesets.Taiko
difficultyHitObjects.Clear();
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.
difficultyHitObjects.Sort((a, b) => a.BaseHitObject.StartTime.CompareTo(b.BaseHitObject.StartTime));
@ -132,7 +131,5 @@ namespace osu.Game.Rulesets.Taiko
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.Taiko.Replays;
using osu.Game.Beatmaps.Legacy;
using osu.Game.Rulesets.Taiko.Beatmaps;
namespace osu.Game.Rulesets.Taiko
{
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[]
{

View File

@ -8,7 +8,6 @@ using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.Taiko.Objects.Drawables;
using osu.Game.Rulesets.Taiko.Scoring;
@ -24,8 +23,8 @@ namespace osu.Game.Rulesets.Taiko.UI
{
public class TaikoRulesetContainer : ScrollingRulesetContainer<TaikoPlayfield, TaikoHitObject>
{
public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
public TaikoRulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
@ -93,8 +92,6 @@ namespace osu.Game.Rulesets.Taiko.UI
public override ScoreProcessor CreateScoreProcessor() => new TaikoScoreProcessor(this);
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(IsForCurrentRuleset);
public override PassThroughInputManager CreateInputManager() => new TaikoInputManager(Ruleset.RulesetInfo);
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
// to simulate setting a replay rather than having the replay already set for us
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
var replay = dummyRulesetContainer.Replay;

View File

@ -22,25 +22,27 @@ namespace osu.Game.Beatmaps
remove => ObjectConverted -= value;
}
/// <summary>
/// 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));
public IBeatmap Beatmap { get; }
/// <summary>
/// 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)
protected BeatmapConverter(IBeatmap beatmap)
{
// We always operate on a clone of the original beatmap, to not modify it game-wide
return ConvertBeatmap(original.Clone());
Beatmap = beatmap;
}
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>
/// 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 Microsoft.EntityFrameworkCore;
using osu.Framework.Audio;
using osu.Framework.Audio.Track;
using osu.Framework.Extensions;
using osu.Framework.Graphics.Textures;
using osu.Framework.Logging;
using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats;
@ -341,9 +343,16 @@ namespace osu.Game.Beatmaps
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.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);
}
@ -351,5 +360,23 @@ namespace osu.Game.Beatmaps
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
using System.Linq;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
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>
/// Processes a post-converted Beatmap.
/// </summary>
/// <typeparam name="TObject">The type of HitObject contained in the Beatmap.</typeparam>
public class BeatmapProcessor<TObject>
where TObject : HitObject
public class BeatmapProcessor : IBeatmapProcessor
{
public IBeatmap Beatmap { get; }
public BeatmapProcessor(IBeatmap beatmap)
{
Beatmap = beatmap;
}
/// <summary>
/// Post-processes a Beatmap to add mode-specific components that aren't added during conversion.
/// <para>
/// An example of such a usage is for combo colours.
/// </para>
/// </summary>
/// <param name="beatmap">The Beatmap to process.</param>
public virtual void PostProcess(Beatmap<TObject> beatmap)
public virtual void PostProcess()
{
IHasComboInformation lastObj = null;
foreach (var obj in beatmap.HitObjects.OfType<IHasComboInformation>())
foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>())
{
if (obj.NewCombo)
{

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
using System.Collections.Generic;
using osu.Game.Rulesets.Mods;
using osu.Framework.Timing;
@ -12,30 +11,17 @@ namespace osu.Game.Beatmaps
{
public abstract class DifficultyCalculator
{
protected double TimeRate = 1;
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 IBeatmap Beatmap;
protected readonly Mod[] Mods;
protected double TimeRate = 1;
protected DifficultyCalculator(IBeatmap beatmap, Mod[] mods = null)
{
Beatmap = beatmap;
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);
PreprocessHitObjects();
}
protected virtual void ApplyMods(Mod[] mods)
@ -43,22 +29,12 @@ namespace osu.Game.Beatmaps
var clock = new StopwatchClock();
mods.OfType<IApplicableToClock>().ForEach(m => m.ApplyToClock(clock));
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 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.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
namespace osu.Game.Beatmaps
@ -53,11 +54,13 @@ namespace osu.Game.Beatmaps
{
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();
}
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";
@ -68,6 +71,14 @@ namespace osu.Game.Beatmaps
: 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>
event Action<HitObject, IEnumerable<HitObject>> ObjectConverted;
IBeatmap Beatmap { get; }
/// <summary>
/// Converts a Beatmap using this Beatmap Converter.
/// Whether <see cref="Beatmap"/> can be converted by this <see cref="IBeatmapConverter"/>.
/// </summary>
/// <param name="beatmap">The un-converted Beatmap.</param>
void Convert(IBeatmap beatmap);
bool CanConvert { get; }
/// <summary>
/// Converts <see cref="Beatmap"/>.
/// </summary>
IBeatmap Convert();
}
}

View File

@ -14,6 +14,8 @@ using osu.Framework.IO.File;
using System.IO;
using osu.Game.IO.Serialization;
using System.Diagnostics;
using osu.Game.Rulesets;
using osu.Game.Rulesets.UI;
using osu.Game.Skinning;
namespace osu.Game.Beatmaps
@ -45,7 +47,7 @@ namespace osu.Game.Beatmaps
}
/// <summary>
/// Saves the <see cref="Beatmap"/>.
/// Saves the <see cref="Beatmaps.Beatmap"/>.
/// </summary>
public void Save()
{
@ -65,7 +67,6 @@ namespace osu.Game.Beatmaps
public bool BeatmapLoaded => beatmap.IsResultAvailable;
public IBeatmap Beatmap => beatmap.Value.Result;
public async Task<IBeatmap> GetBeatmapAsync() => await beatmap.Value;
private readonly AsyncLazy<IBeatmap> beatmap;
private IBeatmap populateBeatmap()
@ -78,6 +79,51 @@ namespace osu.Game.Beatmaps
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 Texture Background => background.Value.Result;
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;
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; }

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}"/>.
/// </summary>
/// <typeparam name="TObject">The type of converted <see cref="HitObject"/>.</typeparam>
public interface IApplicableToBeatmapConverter<TObject> : IApplicableMod
where TObject : HitObject
public interface IApplicableToBeatmapConverter : IApplicableMod
{
/// <summary>
/// Applies this <see cref="Mod"/> to a <see cref="BeatmapConverter{TObject}"/>.
/// </summary>
/// <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>
/// An interface for <see cref="Mod"/>s that can be applied to <see cref="HitObject"/>s.
/// </summary>
public interface IApplicableToHitObject<in TObject> : IApplicableMod
where TObject : HitObject
public interface IApplicableToHitObject : IApplicableMod
{
/// <summary>
/// Applies this <see cref="IApplicableToHitObject{TObject}"/> to a <see cref="HitObject"/>.
/// </summary>
/// <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
/// </summary>
/// <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>
/// <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);
@ -114,7 +117,8 @@ namespace osu.Game.Rulesets
Name = Description,
ShortName = ShortName,
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
using System.Collections.Generic;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects;
namespace osu.Game.Rulesets.Scoring
{
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>();
protected IDictionary<string, double> Attributes => attributes;
protected readonly Beatmap<TObject> Beatmap;
protected readonly IBeatmap Beatmap;
protected readonly Score Score;
protected PerformanceCalculator(Ruleset ruleset, IBeatmap beatmap, Score score)
{
Score = score;
var converter = CreateBeatmapConverter();
foreach (var mod in score.Mods.OfType<IApplicableToBeatmapConverter<TObject>>())
mod.ApplyToBeatmapConverter(converter);
Beatmap = converter.Convert(beatmap);
Beatmap = beatmap;
var diffCalc = ruleset.CreateDifficultyCalculator(beatmap, score.Mods);
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>
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);
protected override Container<Drawable> Content => content;
@ -206,43 +201,18 @@ namespace osu.Game.Rulesets.UI
/// </summary>
/// <param name="ruleset">The ruleset being repesented.</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, bool isForCurrentRuleset)
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap workingBeatmap)
: base(ruleset)
{
Debug.Assert(workingBeatmap != null, "RulesetContainer initialized with a null beatmap.");
WorkingBeatmap = workingBeatmap;
IsForCurrentRuleset = isForCurrentRuleset;
// ReSharper disable once PossibleNullReferenceException
Mods = workingBeatmap.Mods.Value;
RelativeSizeAxes = Axes.Both;
BeatmapConverter<TObject> converter = CreateBeatmapConverter();
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);
Beatmap = (Beatmap<TObject>)workingBeatmap.GetPlayableBeatmap(ruleset.RulesetInfo);
KeyBindingInputManager = CreateInputManager();
KeyBindingInputManager.RelativeSizeAxes = Axes.Both;
@ -277,10 +247,6 @@ namespace osu.Game.Rulesets.UI
if (mods == null)
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>>())
mod.ApplyToRulesetContainer(this);
}
@ -324,13 +290,6 @@ namespace osu.Game.Rulesets.UI
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>
/// Computes the size of the <see cref="Playfield"/> in relative coordinate space after aspect adjustments.
/// </summary>
@ -344,12 +303,6 @@ namespace osu.Game.Rulesets.UI
/// </summary>
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>
/// Creates a DrawableHitObject from a HitObject.
/// </summary>
@ -377,9 +330,8 @@ namespace osu.Game.Rulesets.UI
/// </summary>
/// <param name="ruleset">The ruleset being repesented.</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, bool isForCurrentRuleset)
: base(ruleset, beatmap, isForCurrentRuleset)
protected RulesetContainer(Ruleset ruleset, WorkingBeatmap beatmap)
: base(ruleset, beatmap)
{
}
}

View File

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

View File

@ -107,7 +107,7 @@ namespace osu.Game.Screens.Play
try
{
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working, ruleset.ID == beatmap.BeatmapInfo.Ruleset.ID);
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(working);
}
catch (BeatmapInvalidForRulesetException)
{
@ -115,7 +115,7 @@ namespace osu.Game.Screens.Play
// let's try again forcing the beatmap's ruleset.
ruleset = beatmap.BeatmapInfo.Ruleset;
rulesetInstance = ruleset.CreateInstance();
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap, true);
RulesetContainer = rulesetInstance.CreateRulesetContainerWith(Beatmap);
}
if (!RulesetContainer.Objects.Any())

View File

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