Merge branch 'master' into fix-quick-retry-music-playback

This commit is contained in:
Bartłomiej Dach
2020-10-07 20:57:10 +02:00
committed by GitHub
19 changed files with 212 additions and 61 deletions

View File

@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Catch
public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new CatchLegacySkinTransformer(source); public override ISkin CreateLegacySkinProvider(ISkinSource source, IBeatmap beatmap) => new CatchLegacySkinTransformer(source);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new CatchPerformanceCalculator(this, beatmap, score); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new CatchPerformanceCalculator(this, attributes, score);
public int LegacyID => 2; public int LegacyID => 2;

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -25,8 +24,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty
private int tinyTicksMissed; private int tinyTicksMissed;
private int misses; private int misses;
public CatchPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score) public CatchPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
: base(ruleset, beatmap, score) : base(ruleset, attributes, score)
{ {
} }

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -29,8 +28,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty
private int countMeh; private int countMeh;
private int countMiss; private int countMiss;
public ManiaPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score) public ManiaPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
: base(ruleset, beatmap, score) : base(ruleset, attributes, score)
{ {
} }

View File

@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania
public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this); public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new ManiaBeatmapConverter(beatmap, this);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new ManiaPerformanceCalculator(this, beatmap, score); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new ManiaPerformanceCalculator(this, attributes, score);
public const string SHORT_NAME = "mania"; public const string SHORT_NAME = "mania";

View File

@ -11,5 +11,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
public double SpeedStrain; public double SpeedStrain;
public double ApproachRate; public double ApproachRate;
public double OverallDifficulty; public double OverallDifficulty;
public int HitCircleCount;
} }
} }

View File

@ -47,6 +47,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above) // Add the ticks + tail of the slider. 1 is subtracted because the head circle would be counted twice (once for the slider itself in the line above)
maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1); maxCombo += beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
int hitCirclesCount = beatmap.HitObjects.Count(h => h is HitCircle);
return new OsuDifficultyAttributes return new OsuDifficultyAttributes
{ {
StarRating = starRating, StarRating = starRating,
@ -56,6 +58,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5, ApproachRate = preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5,
OverallDifficulty = (80 - hitWindowGreat) / 6, OverallDifficulty = (80 - hitWindowGreat) / 6,
MaxCombo = maxCombo, MaxCombo = maxCombo,
HitCircleCount = hitCirclesCount,
Skills = skills Skills = skills
}; };
} }

View File

@ -5,11 +5,9 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -19,9 +17,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{ {
public new OsuDifficultyAttributes Attributes => (OsuDifficultyAttributes)base.Attributes; public new OsuDifficultyAttributes Attributes => (OsuDifficultyAttributes)base.Attributes;
private readonly int countHitCircles;
private readonly int beatmapMaxCombo;
private Mod[] mods; private Mod[] mods;
private double accuracy; private double accuracy;
@ -31,14 +26,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
private int countMeh; private int countMeh;
private int countMiss; private int countMiss;
public OsuPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score) public OsuPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
: base(ruleset, beatmap, score) : base(ruleset, attributes, score)
{ {
countHitCircles = Beatmap.HitObjects.Count(h => h is HitCircle);
beatmapMaxCombo = Beatmap.HitObjects.Count;
// Add the ticks + tail of the slider. 1 is subtracted because the "headcircle" would be counted twice (once for the slider itself in the line above)
beatmapMaxCombo += Beatmap.HitObjects.OfType<Slider>().Sum(s => s.NestedHitObjects.Count - 1);
} }
public override double Calculate(Dictionary<string, double> categoryRatings = null) public override double Calculate(Dictionary<string, double> categoryRatings = null)
@ -81,7 +71,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
categoryRatings.Add("Accuracy", accuracyValue); categoryRatings.Add("Accuracy", accuracyValue);
categoryRatings.Add("OD", Attributes.OverallDifficulty); categoryRatings.Add("OD", Attributes.OverallDifficulty);
categoryRatings.Add("AR", Attributes.ApproachRate); categoryRatings.Add("AR", Attributes.ApproachRate);
categoryRatings.Add("Max Combo", beatmapMaxCombo); categoryRatings.Add("Max Combo", Attributes.MaxCombo);
} }
return totalValue; return totalValue;
@ -106,8 +96,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
aimValue *= Math.Pow(0.97, countMiss); aimValue *= Math.Pow(0.97, countMiss);
// Combo scaling // Combo scaling
if (beatmapMaxCombo > 0) if (Attributes.MaxCombo > 0)
aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0); aimValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
double approachRateFactor = 1.0; double approachRateFactor = 1.0;
@ -154,8 +144,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty
speedValue *= Math.Pow(0.97, countMiss); speedValue *= Math.Pow(0.97, countMiss);
// Combo scaling // Combo scaling
if (beatmapMaxCombo > 0) if (Attributes.MaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(beatmapMaxCombo, 0.8), 1.0); speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8) / Math.Pow(Attributes.MaxCombo, 0.8), 1.0);
double approachRateFactor = 1.0; double approachRateFactor = 1.0;
if (Attributes.ApproachRate > 10.33) if (Attributes.ApproachRate > 10.33)
@ -178,7 +168,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
{ {
// This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window // This percentage only considers HitCircles of any value - in this part of the calculation we focus on hitting the timing hit window
double betterAccuracyPercentage; double betterAccuracyPercentage;
int amountHitObjectsWithAccuracy = countHitCircles; int amountHitObjectsWithAccuracy = Attributes.HitCircleCount;
if (amountHitObjectsWithAccuracy > 0) if (amountHitObjectsWithAccuracy > 0)
betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6); betterAccuracyPercentage = ((countGreat - (totalHits - amountHitObjectsWithAccuracy)) * 6 + countOk * 2 + countMeh) / (double)(amountHitObjectsWithAccuracy * 6);

View File

@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap); public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new OsuDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new OsuPerformanceCalculator(this, beatmap, score); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new OsuPerformanceCalculator(this, attributes, score);
public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this); public override HitObjectComposer CreateHitObjectComposer() => new OsuHitObjectComposer(this);

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Difficulty;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
@ -24,8 +23,8 @@ namespace osu.Game.Rulesets.Taiko.Difficulty
private int countMeh; private int countMeh;
private int countMiss; private int countMiss;
public TaikoPerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score) public TaikoPerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
: base(ruleset, beatmap, score) : base(ruleset, attributes, score)
{ {
} }

View File

@ -7,6 +7,7 @@ using osu.Framework.Audio.Sample;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.UI; using osu.Game.Rulesets.Taiko.UI;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -14,13 +15,29 @@ namespace osu.Game.Rulesets.Taiko.Skinning
{ {
public class TaikoLegacySkinTransformer : LegacySkinTransformer public class TaikoLegacySkinTransformer : LegacySkinTransformer
{ {
private Lazy<bool> hasExplosion;
public TaikoLegacySkinTransformer(ISkinSource source) public TaikoLegacySkinTransformer(ISkinSource source)
: base(source) : base(source)
{ {
Source.SourceChanged += sourceChanged;
sourceChanged();
}
private void sourceChanged()
{
hasExplosion = new Lazy<bool>(() => Source.GetTexture(getHitName(TaikoSkinComponents.TaikoExplosionGreat)) != null);
} }
public override Drawable GetDrawableComponent(ISkinComponent component) public override Drawable GetDrawableComponent(ISkinComponent component)
{ {
if (component is GameplaySkinComponent<HitResult>)
{
// if a taiko skin is providing explosion sprites, hide the judgements completely
if (hasExplosion.Value)
return Drawable.Empty();
}
if (!(component is TaikoSkinComponent taikoComponent)) if (!(component is TaikoSkinComponent taikoComponent))
return null; return null;
@ -87,10 +104,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning
var hitName = getHitName(taikoComponent.Component); var hitName = getHitName(taikoComponent.Component);
var hitSprite = this.GetAnimation(hitName, true, false); var hitSprite = this.GetAnimation(hitName, true, false);
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
if (hitSprite != null) if (hitSprite != null)
{
var strongHitSprite = this.GetAnimation($"{hitName}k", true, false);
return new LegacyHitExplosion(hitSprite, strongHitSprite); return new LegacyHitExplosion(hitSprite, strongHitSprite);
}
return null; return null;

View File

@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Taiko
public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap); public override DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap) => new TaikoDifficultyCalculator(this, beatmap);
public override PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => new TaikoPerformanceCalculator(this, beatmap, score); public override PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => new TaikoPerformanceCalculator(this, attributes, score);
public int LegacyID => 1; public int LegacyID => 1;

View File

@ -44,7 +44,7 @@ namespace osu.Game.Tests.Editing
{ {
HitObjects = HitObjects =
{ {
new HitCircle { StartTime = 1000 } new HitCircle { StartTime = 1000, NewCombo = true }
} }
}; };
@ -56,7 +56,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000, NewCombo = true },
new HitCircle { StartTime = 3000 }, new HitCircle { StartTime = 3000 },
}); });
@ -78,7 +78,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000, NewCombo = true },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
new HitCircle { StartTime = 3000 }, new HitCircle { StartTime = 3000 },
}); });
@ -100,7 +100,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000, NewCombo = true },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
new HitCircle { StartTime = 3000 }, new HitCircle { StartTime = 3000 },
}); });
@ -109,7 +109,7 @@ namespace osu.Game.Tests.Editing
{ {
HitObjects = HitObjects =
{ {
new HitCircle { StartTime = 500 }, new HitCircle { StartTime = 500, NewCombo = true },
(OsuHitObject)current.HitObjects[1], (OsuHitObject)current.HitObjects[1],
(OsuHitObject)current.HitObjects[2], (OsuHitObject)current.HitObjects[2],
} }
@ -123,7 +123,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000, NewCombo = true },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
new HitCircle { StartTime = 3000 }, new HitCircle { StartTime = 3000 },
}); });
@ -146,7 +146,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new OsuHitObject[] current.AddRange(new OsuHitObject[]
{ {
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000, NewCombo = true },
new Slider new Slider
{ {
StartTime = 2000, StartTime = 2000,
@ -188,7 +188,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000, NewCombo = true },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
new HitCircle { StartTime = 3000 }, new HitCircle { StartTime = 3000 },
}); });
@ -197,7 +197,7 @@ namespace osu.Game.Tests.Editing
{ {
HitObjects = HitObjects =
{ {
new HitCircle { StartTime = 500 }, new HitCircle { StartTime = 500, NewCombo = true },
(OsuHitObject)current.HitObjects[0], (OsuHitObject)current.HitObjects[0],
new HitCircle { StartTime = 1500 }, new HitCircle { StartTime = 1500 },
(OsuHitObject)current.HitObjects[1], (OsuHitObject)current.HitObjects[1],
@ -216,7 +216,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 500 }, new HitCircle { StartTime = 500, NewCombo = true },
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000 },
new HitCircle { StartTime = 1500 }, new HitCircle { StartTime = 1500 },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
@ -226,6 +226,9 @@ namespace osu.Game.Tests.Editing
new HitCircle { StartTime = 3500 }, new HitCircle { StartTime = 3500 },
}); });
var patchedFirst = (HitCircle)current.HitObjects[1];
patchedFirst.NewCombo = true;
var patch = new OsuBeatmap var patch = new OsuBeatmap
{ {
HitObjects = HitObjects =
@ -244,7 +247,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 500 }, new HitCircle { StartTime = 500, NewCombo = true },
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000 },
new HitCircle { StartTime = 1500 }, new HitCircle { StartTime = 1500 },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
@ -277,7 +280,7 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 500 }, new HitCircle { StartTime = 500, NewCombo = true },
new HitCircle { StartTime = 1000 }, new HitCircle { StartTime = 1000 },
new HitCircle { StartTime = 1500 }, new HitCircle { StartTime = 1500 },
new HitCircle { StartTime = 2000 }, new HitCircle { StartTime = 2000 },
@ -291,7 +294,7 @@ namespace osu.Game.Tests.Editing
{ {
HitObjects = HitObjects =
{ {
new HitCircle { StartTime = 750 }, new HitCircle { StartTime = 750, NewCombo = true },
(OsuHitObject)current.HitObjects[1], (OsuHitObject)current.HitObjects[1],
(OsuHitObject)current.HitObjects[4], (OsuHitObject)current.HitObjects[4],
(OsuHitObject)current.HitObjects[5], (OsuHitObject)current.HitObjects[5],
@ -309,20 +312,20 @@ namespace osu.Game.Tests.Editing
{ {
current.AddRange(new[] current.AddRange(new[]
{ {
new HitCircle { StartTime = 500, Position = new Vector2(50) }, new HitCircle { StartTime = 500, Position = new Vector2(50), NewCombo = true },
new HitCircle { StartTime = 500, Position = new Vector2(100) }, new HitCircle { StartTime = 500, Position = new Vector2(100), NewCombo = true },
new HitCircle { StartTime = 500, Position = new Vector2(150) }, new HitCircle { StartTime = 500, Position = new Vector2(150), NewCombo = true },
new HitCircle { StartTime = 500, Position = new Vector2(200) }, new HitCircle { StartTime = 500, Position = new Vector2(200), NewCombo = true },
}); });
var patch = new OsuBeatmap var patch = new OsuBeatmap
{ {
HitObjects = HitObjects =
{ {
new HitCircle { StartTime = 500, Position = new Vector2(150) }, new HitCircle { StartTime = 500, Position = new Vector2(150), NewCombo = true },
new HitCircle { StartTime = 500, Position = new Vector2(100) }, new HitCircle { StartTime = 500, Position = new Vector2(100), NewCombo = true },
new HitCircle { StartTime = 500, Position = new Vector2(50) }, new HitCircle { StartTime = 500, Position = new Vector2(50), NewCombo = true },
new HitCircle { StartTime = 500, Position = new Vector2(200) }, new HitCircle { StartTime = 500, Position = new Vector2(200), NewCombo = true },
} }
}; };

View File

@ -22,8 +22,18 @@ namespace osu.Game.Beatmaps
{ {
IHasComboInformation lastObj = null; IHasComboInformation lastObj = null;
bool isFirst = true;
foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>()) foreach (var obj in Beatmap.HitObjects.OfType<IHasComboInformation>())
{ {
if (isFirst)
{
obj.NewCombo = true;
// first hitobject should always be marked as a new combo for sanity.
isFirst = false;
}
if (obj.NewCombo) if (obj.NewCombo)
{ {
obj.IndexInCurrentCombo = 0; obj.IndexInCurrentCombo = 0;

View File

@ -1,11 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Scoring; using osu.Game.Scoring;
@ -16,19 +16,16 @@ namespace osu.Game.Rulesets.Difficulty
protected readonly DifficultyAttributes Attributes; protected readonly DifficultyAttributes Attributes;
protected readonly Ruleset Ruleset; protected readonly Ruleset Ruleset;
protected readonly IBeatmap Beatmap;
protected readonly ScoreInfo Score; protected readonly ScoreInfo Score;
protected double TimeRate { get; private set; } = 1; protected double TimeRate { get; private set; } = 1;
protected PerformanceCalculator(Ruleset ruleset, WorkingBeatmap beatmap, ScoreInfo score) protected PerformanceCalculator(Ruleset ruleset, DifficultyAttributes attributes, ScoreInfo score)
{ {
Ruleset = ruleset; Ruleset = ruleset;
Score = score; Score = score;
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, score.Mods); Attributes = attributes ?? throw new ArgumentNullException(nameof(attributes));
Attributes = ruleset.CreateDifficultyCalculator(beatmap).Calculate(score.Mods);
ApplyMods(score.Mods); ApplyMods(score.Mods);
} }

View File

@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Objects.Drawables
// apply any custom state overrides // apply any custom state overrides
ApplyCustomUpdateState?.Invoke(this, newState); ApplyCustomUpdateState?.Invoke(this, newState);
if (newState == ArmedState.Hit) if (!force && newState == ArmedState.Hit)
PlaySamples(); PlaySamples();
} }

View File

@ -158,7 +158,28 @@ namespace osu.Game.Rulesets
public abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap); public abstract DifficultyCalculator CreateDifficultyCalculator(WorkingBeatmap beatmap);
public virtual PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score) => null; /// <summary>
/// Optionally creates a <see cref="PerformanceCalculator"/> to generate performance data from the provided score.
/// </summary>
/// <param name="attributes">Difficulty attributes for the beatmap related to the provided score.</param>
/// <param name="score">The score to be processed.</param>
/// <returns>A performance calculator instance for the provided score.</returns>
[CanBeNull]
public virtual PerformanceCalculator CreatePerformanceCalculator(DifficultyAttributes attributes, ScoreInfo score) => null;
/// <summary>
/// Optionally creates a <see cref="PerformanceCalculator"/> to generate performance data from the provided score.
/// </summary>
/// <param name="beatmap">The beatmap to use as a source for generating <see cref="DifficultyAttributes"/>.</param>
/// <param name="score">The score to be processed.</param>
/// <returns>A performance calculator instance for the provided score.</returns>
[CanBeNull]
public PerformanceCalculator CreatePerformanceCalculator(WorkingBeatmap beatmap, ScoreInfo score)
{
var difficultyCalculator = CreateDifficultyCalculator(beatmap);
var difficultyAttributes = difficultyCalculator.Calculate(score.Mods);
return CreatePerformanceCalculator(difficultyAttributes, score);
}
public virtual HitObjectComposer CreateHitObjectComposer() => null; public virtual HitObjectComposer CreateHitObjectComposer() => null;

View File

@ -315,5 +315,14 @@ namespace osu.Game.Screens.Edit
public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor; public double GetBeatLengthAtTime(double referenceTime) => ControlPointInfo.TimingPointAt(referenceTime).BeatLength / BeatDivisor;
public int BeatDivisor => beatDivisor?.Value ?? 1; public int BeatDivisor => beatDivisor?.Value ?? 1;
/// <summary>
/// Update all hit objects with potentially changed difficulty or control point data.
/// </summary>
public void UpdateBeatmap()
{
foreach (var h in HitObjects)
pendingUpdates.Add(h);
}
} }
} }

View File

@ -0,0 +1,99 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterfaceV2;
namespace osu.Game.Screens.Edit.Setup
{
internal class DifficultySection : SetupSection
{
[Resolved]
private EditorBeatmap editorBeatmap { get; set; }
private LabelledSliderBar<float> circleSizeSlider;
private LabelledSliderBar<float> healthDrainSlider;
private LabelledSliderBar<float> approachRateSlider;
private LabelledSliderBar<float> overallDifficultySlider;
[BackgroundDependencyLoader]
private void load()
{
Children = new Drawable[]
{
new OsuSpriteText
{
Text = "Difficulty settings"
},
circleSizeSlider = new LabelledSliderBar<float>
{
Label = "Object Size",
Description = "The size of all hit objects",
Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 2,
MaxValue = 7,
Precision = 0.1f,
}
},
healthDrainSlider = new LabelledSliderBar<float>
{
Label = "Health Drain",
Description = "The rate of passive health drain throughout playable time",
Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.DrainRate)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 0.1f,
}
},
approachRateSlider = new LabelledSliderBar<float>
{
Label = "Approach Rate",
Description = "The speed at which objects are presented to the player",
Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.ApproachRate)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 0.1f,
}
},
overallDifficultySlider = new LabelledSliderBar<float>
{
Label = "Overall Difficulty",
Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)",
Current = new BindableFloat(Beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty)
{
Default = BeatmapDifficulty.DEFAULT_DIFFICULTY,
MinValue = 0,
MaxValue = 10,
Precision = 0.1f,
}
},
};
foreach (var item in Children.OfType<LabelledSliderBar<float>>())
item.Current.ValueChanged += onValueChanged;
}
private void onValueChanged(ValueChangedEvent<float> args)
{
// for now, update these on commit rather than making BeatmapMetadata bindables.
// after switching database engines we can reconsider if switching to bindables is a good direction.
Beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize = circleSizeSlider.Current.Value;
Beatmap.Value.BeatmapInfo.BaseDifficulty.DrainRate = healthDrainSlider.Current.Value;
Beatmap.Value.BeatmapInfo.BaseDifficulty.ApproachRate = approachRateSlider.Current.Value;
Beatmap.Value.BeatmapInfo.BaseDifficulty.OverallDifficulty = overallDifficultySlider.Current.Value;
editorBeatmap.UpdateBeatmap();
}
}
}

View File

@ -52,6 +52,7 @@ namespace osu.Game.Screens.Edit.Setup
{ {
new ResourcesSection(), new ResourcesSection(),
new MetadataSection(), new MetadataSection(),
new DifficultySection(),
} }
}, },
} }