Merge pull request #1516 from smoogipoo/mod-improvements

Various improvements to make DifficultyCalculator support mods
This commit is contained in:
Dean Herbert 2017-11-17 15:35:05 +09:00 committed by GitHub
commit c1fd2a3950
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
19 changed files with 125 additions and 89 deletions

View File

@ -14,11 +14,8 @@ namespace osu.Game.Rulesets.Catch
{
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
{
return 0;
}
public override double Calculate(Dictionary<string, string> categoryDifficulty = null) => 0;
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter() => new CatchBeatmapConverter();
protected override BeatmapConverter<CatchBaseHit> CreateBeatmapConverter(Beatmap beatmap) => new CatchBeatmapConverter();
}
}
}

View File

@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Catch
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_fruits_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new CatchDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new CatchDifficultyCalculator(beatmap);
public override int LegacyID => 2;

View File

@ -16,11 +16,8 @@ namespace osu.Game.Rulesets.Mania
{
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
{
return 0;
}
public override double Calculate(Dictionary<string, string> categoryDifficulty = null) => 0;
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter() => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
protected override BeatmapConverter<ManiaHitObject> CreateBeatmapConverter(Beatmap beatmap) => new ManiaBeatmapConverter(true, (int)Math.Max(1, Math.Round(beatmap.BeatmapInfo.BaseDifficulty.CircleSize)));
}
}

View File

@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Mania
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_mania_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new ManiaDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new ManiaDifficultyCalculator(beatmap);
public override int LegacyID => 3;

View File

@ -86,18 +86,17 @@ namespace osu.Game.Rulesets.Mania.Mods
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight) };
}
public class ManiaModRandom : Mod, IApplicableMod<ManiaHitObject>
public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject>
{
public override string Name => "Random";
public override string ShortenedName => "RD";
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override FontAwesome Icon => FontAwesome.fa_osu_dice;
public override string Description => @"Shuffle around the notes!";
public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer)
{
int availableColumns = ((ManiaRulesetContainer)rulesetContainer).AvailableColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
@ -188,6 +187,7 @@ namespace osu.Game.Rulesets.Mania.Mods
base.ApplyToRulesetContainer(rulesetContainer);
}
protected override Score CreateReplayScore(Beatmap<ManiaHitObject> beatmap) => new Score
{
User = new User { Username = "osu!topus!" },

View File

@ -9,7 +9,6 @@ using osu.Game.Rulesets.Osu.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@ -33,22 +32,29 @@ namespace osu.Game.Rulesets.Osu.Mods
public override double ScoreMultiplier => 1.06;
}
public class OsuModHardRock : ModHardRock, IApplicableMod<OsuHitObject>
public class OsuModHardRock : ModHardRock, IApplicableToHitObject<OsuHitObject>
{
public override double ScoreMultiplier => 1.06;
public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<OsuHitObject> rulesetContainer)
public void ApplyToHitObject(OsuHitObject hitObject)
{
hitObject.Position = new Vector2(hitObject.Position.X, OsuPlayfield.BASE_SIZE.Y - hitObject.Y);
var slider = hitObject as Slider;
if (slider == null)
return;
var newControlPoints = new List<Vector2>();
slider.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
slider.ControlPoints = newControlPoints;
slider.Curve?.Calculate(); // Recalculate the slider curve
}
public void ApplyToHitObjects(RulesetContainer<OsuHitObject> rulesetContainer)
{
rulesetContainer.Objects.OfType<OsuHitObject>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Y));
rulesetContainer.Objects.OfType<Slider>().ForEach(s =>
{
var newControlPoints = new List<Vector2>();
s.ControlPoints.ForEach(c => newControlPoints.Add(new Vector2(c.X, OsuPlayfield.BASE_SIZE.Y - c.Y)));
s.ControlPoints = newControlPoints;
s.Curve?.Calculate(); // Recalculate the slider curve
});
}
}

View File

@ -4,6 +4,7 @@
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;
@ -16,19 +17,25 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
private const int section_length = 400;
private const double difficulty_multiplier = 0.0675;
public OsuDifficultyCalculator(Beatmap beatmap) : base(beatmap)
public OsuDifficultyCalculator(Beatmap beatmap)
: base(beatmap)
{
}
public OsuDifficultyCalculator(Beatmap beatmap, Mod[] mods)
: base(beatmap, mods)
{
}
protected override void PreprocessHitObjects()
{
foreach (OsuHitObject h in Objects)
foreach (OsuHitObject h in Beatmap.HitObjects)
(h as Slider)?.Curve?.Calculate();
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
public override double Calculate(Dictionary<string, string> categoryDifficulty = null)
{
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Objects);
OsuDifficultyBeatmap beatmap = new OsuDifficultyBeatmap(Beatmap.HitObjects);
Skill[] skills =
{
new Aim(),
@ -67,6 +74,6 @@ namespace osu.Game.Rulesets.Osu.OsuDifficulty
return starRating;
}
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter() => new OsuBeatmapConverter();
protected override BeatmapConverter<OsuHitObject> CreateBeatmapConverter(Beatmap beatmap) => new OsuBeatmapConverter();
}
}

View File

@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_osu_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new OsuDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new OsuDifficultyCalculator(beatmap, mods);
public override string Description => "osu!";

View File

@ -36,12 +36,12 @@ namespace osu.Game.Rulesets.Taiko
{
}
protected override double CalculateInternal(Dictionary<string, string> categoryDifficulty)
public override double Calculate(Dictionary<string, string> categoryDifficulty = null)
{
// Fill our custom DifficultyHitObject class, that carries additional information
difficultyHitObjects.Clear();
foreach (var hitObject in Objects)
foreach (var hitObject in Beatmap.HitObjects)
difficultyHitObjects.Add(new TaikoHitObjectDifficulty(hitObject));
// Sort DifficultyHitObjects by StartTime of the HitObjects - just to make sure.
@ -134,6 +134,6 @@ namespace osu.Game.Rulesets.Taiko
return difficulty;
}
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter() => new TaikoBeatmapConverter(true);
protected override BeatmapConverter<TaikoHitObject> CreateBeatmapConverter(Beatmap beatmap) => new TaikoBeatmapConverter(true);
}
}

View File

@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko
public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_osu_taiko_o };
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => new TaikoDifficultyCalculator(beatmap);
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => new TaikoDifficultyCalculator(beatmap);
public override int LegacyID => 1;

View File

@ -3,6 +3,10 @@
using osu.Game.Rulesets.Objects;
using System.Collections.Generic;
using osu.Game.Rulesets.Mods;
using osu.Framework.Timing;
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
namespace osu.Game.Beatmaps
{
@ -10,45 +14,46 @@ namespace osu.Game.Beatmaps
{
protected double TimeRate = 1;
protected abstract double CalculateInternal(Dictionary<string, string> categoryDifficulty);
private void loadTiming()
{
// TODO: Handle mods
const int audio_rate = 100;
TimeRate = audio_rate / 100.0;
}
public double Calculate(Dictionary<string, string> categoryDifficulty = null)
{
loadTiming();
double difficulty = CalculateInternal(categoryDifficulty);
return difficulty;
}
public abstract double Calculate(Dictionary<string, string> categoryDifficulty = null);
}
public abstract class DifficultyCalculator<T> : DifficultyCalculator where T : HitObject
{
protected readonly Beatmap Beatmap;
protected readonly Beatmap<T> Beatmap;
protected readonly Mod[] Mods;
protected List<T> Objects;
protected DifficultyCalculator(Beatmap beatmap)
protected DifficultyCalculator(Beatmap beatmap, Mod[] mods = null)
{
Beatmap = beatmap;
Beatmap = CreateBeatmapConverter(beatmap).Convert(beatmap);
Mods = mods ?? new Mod[0];
Objects = CreateBeatmapConverter().Convert(beatmap).HitObjects;
foreach (var h in Objects)
h.ApplyDefaults(beatmap.ControlPointInfo, beatmap.BeatmapInfo.BaseDifficulty);
ApplyMods(Mods);
PreprocessHitObjects();
}
protected virtual void ApplyMods(Mod[] mods)
{
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 mod in mods.OfType<IApplicableToHitObject<T>>())
foreach (var obj in Beatmap.HitObjects)
mod.ApplyToHitObject(obj);
foreach (var h in Beatmap.HitObjects)
h.ApplyDefaults(Beatmap.ControlPointInfo, Beatmap.BeatmapInfo.BaseDifficulty);
}
protected virtual void PreprocessHitObjects()
{
}
protected abstract BeatmapConverter<T> CreateBeatmapConverter();
protected abstract BeatmapConverter<T> CreateBeatmapConverter(Beatmap beatmap);
}
}

View File

@ -59,7 +59,7 @@ namespace osu.Game.Beatmaps
throw new NotImplementedException();
}
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap) => null;
public override DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null) => null;
public override string Description => "dummy";

View File

@ -1,22 +0,0 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for mods that are applied to a RulesetContainer.
/// </summary>
/// <typeparam name="TObject">The type of HitObject the RulesetContainer contains.</typeparam>
public interface IApplicableMod<TObject>
where TObject : HitObject
{
/// <summary>
/// Applies the mod to a RulesetContainer.
/// </summary>
/// <param name="rulesetContainer">The RulesetContainer to apply the mod to.</param>
void ApplyToRulesetContainer(RulesetContainer<TObject> rulesetContainer);
}
}

View File

@ -0,0 +1,20 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
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>
where TObject : HitObject
{
/// <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);
}
}

View File

@ -0,0 +1,21 @@
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
{
/// <summary>
/// An interface for <see cref="Mod"/>s that can be applied to <see cref="RulesetContainer"/>s.
/// </summary>
public interface IApplicableToRulesetContainer<TObject>
where TObject : HitObject
{
/// <summary>
/// Applies this <see cref="IApplicableToRulesetContainer{TObject}"/> to a <see cref="RulesetContainer{TObject}"/>.
/// </summary>
/// <param name="rulesetContainer">The <see cref="RulesetContainer{TObject}"/> to apply to.</param>
void ApplyToRulesetContainer(RulesetContainer<TObject> rulesetContainer);
}
}

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mods
{
public abstract class ModAutoplay<T> : ModAutoplay, IApplicableMod<T>
public abstract class ModAutoplay<T> : ModAutoplay, IApplicableToRulesetContainer<T>
where T : HitObject
{
protected abstract Score CreateReplayScore(Beatmap<T> beatmap);
@ -30,4 +30,4 @@ namespace osu.Game.Rulesets.Mods
public override double ScoreMultiplier => 0;
public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModSuddenDeath), typeof(ModNoFail) };
}
}
}

View File

@ -47,7 +47,7 @@ namespace osu.Game.Rulesets
/// <returns></returns>
public abstract RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset);
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap);
public abstract DifficultyCalculator CreateDifficultyCalculator(Beatmap beatmap, Mod[] mods = null);
public virtual Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.fa_question_circle };

View File

@ -212,7 +212,11 @@ namespace osu.Game.Rulesets.UI
if (mods == null)
return;
foreach (var mod in mods.OfType<IApplicableMod<TObject>>())
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);
}

View File

@ -549,9 +549,10 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Rulesets\Judgements\DrawableJudgement.cs" />
<Compile Include="Rulesets\Judgements\Judgement.cs" />
<Compile Include="Rulesets\Mods\IApplicableMod.cs" />
<Compile Include="Rulesets\Mods\IApplicableToClock.cs" />
<Compile Include="Rulesets\Mods\IApplicableToDifficulty.cs" />
<Compile Include="Rulesets\Mods\IApplicableToHitObject.cs" />
<Compile Include="Rulesets\Mods\IApplicableToRulesetContainer.cs" />
<Compile Include="Rulesets\Mods\Mod.cs" />
<Compile Include="Rulesets\Mods\ModAutoplay.cs" />
<Compile Include="Rulesets\Mods\ModCinema.cs" />