mirror of
https://github.com/ppy/osu
synced 2024-12-15 03:16:17 +00:00
Merge pull request #1516 from smoogipoo/mod-improvements
Various improvements to make DifficultyCalculator support mods
This commit is contained in:
commit
c1fd2a3950
@ -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();
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
||||
|
@ -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)));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -86,7 +86,7 @@ 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";
|
||||
@ -97,7 +97,6 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
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!" },
|
||||
|
@ -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
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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!";
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
20
osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
Normal file
20
osu.Game/Rulesets/Mods/IApplicableToHitObject.cs
Normal 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);
|
||||
}
|
||||
}
|
21
osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs
Normal file
21
osu.Game/Rulesets/Mods/IApplicableToRulesetContainer.cs
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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 };
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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" />
|
||||
|
Loading…
Reference in New Issue
Block a user