mirror of
https://github.com/ppy/osu.git
synced 2025-03-05 13:13:22 +08:00
Merge pull request #14919 from smoogipoo/realtime-pp-display
Implement real-time PP counter
This commit is contained in:
commit
6f7b8293af
@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
Mods = mods,
|
Mods = mods,
|
||||||
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
// Todo: This int cast is temporary to achieve 1:1 results with osu!stable, and should be removed in the future
|
||||||
GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate),
|
GreatHitWindow = (int)Math.Ceiling(getHitWindow300(mods) / clockRate),
|
||||||
ScoreMultiplier = getScoreMultiplier(beatmap, mods),
|
ScoreMultiplier = getScoreMultiplier(mods),
|
||||||
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
|
MaxCombo = beatmap.HitObjects.Sum(h => h is HoldNote ? 2 : 1),
|
||||||
Skills = skills
|
Skills = skills
|
||||||
};
|
};
|
||||||
@ -70,7 +70,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
|
|
||||||
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
protected override Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate) => new Skill[]
|
||||||
{
|
{
|
||||||
new Strain(mods, ((ManiaBeatmap)beatmap).TotalColumns)
|
new Strain(mods, ((ManiaBeatmap)Beatmap).TotalColumns)
|
||||||
};
|
};
|
||||||
|
|
||||||
protected override Mod[] DifficultyAdjustmentMods
|
protected override Mod[] DifficultyAdjustmentMods
|
||||||
@ -138,7 +138,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private double getScoreMultiplier(IBeatmap beatmap, Mod[] mods)
|
private double getScoreMultiplier(Mod[] mods)
|
||||||
{
|
{
|
||||||
double scoreMultiplier = 1;
|
double scoreMultiplier = 1;
|
||||||
|
|
||||||
@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var maniaBeatmap = (ManiaBeatmap)beatmap;
|
var maniaBeatmap = (ManiaBeatmap)Beatmap;
|
||||||
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
|
int diff = maniaBeatmap.TotalColumns - maniaBeatmap.OriginalTotalColumns;
|
||||||
|
|
||||||
if (diff > 0)
|
if (diff > 0)
|
||||||
|
@ -42,6 +42,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
public void TestEmptyLegacyBeatmapSkinFallsBack()
|
public void TestEmptyLegacyBeatmapSkinFallsBack()
|
||||||
{
|
{
|
||||||
CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null));
|
CreateSkinTest(SkinInfo.Default, () => new LegacyBeatmapSkin(new BeatmapInfo(), null, null));
|
||||||
|
AddUntilStep("wait for hud load", () => Player.ChildrenOfType<SkinnableTargetContainer>().All(c => c.ComponentsLoaded));
|
||||||
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
|
AddAssert("hud from default skin", () => AssertComponentsFromExpectedSource(SkinnableTarget.MainHUDComponents, skinManager.CurrentSkin.Value));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,18 +85,18 @@ namespace osu.Game.Tests.Visual.Gameplay
|
|||||||
Remove(expectedComponentsAdjustmentContainer);
|
Remove(expectedComponentsAdjustmentContainer);
|
||||||
|
|
||||||
return almostEqual(actualInfo, expectedInfo);
|
return almostEqual(actualInfo, expectedInfo);
|
||||||
|
|
||||||
static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
|
|
||||||
other != null
|
|
||||||
&& info.Type == other.Type
|
|
||||||
&& info.Anchor == other.Anchor
|
|
||||||
&& info.Origin == other.Origin
|
|
||||||
&& Precision.AlmostEquals(info.Position, other.Position)
|
|
||||||
&& Precision.AlmostEquals(info.Scale, other.Scale)
|
|
||||||
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
|
|
||||||
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static bool almostEqual(SkinnableInfo info, SkinnableInfo other) =>
|
||||||
|
other != null
|
||||||
|
&& info.Type == other.Type
|
||||||
|
&& info.Anchor == other.Anchor
|
||||||
|
&& info.Origin == other.Origin
|
||||||
|
&& Precision.AlmostEquals(info.Position, other.Position, 1)
|
||||||
|
&& Precision.AlmostEquals(info.Scale, other.Scale)
|
||||||
|
&& Precision.AlmostEquals(info.Rotation, other.Rotation)
|
||||||
|
&& info.Children.SequenceEqual(other.Children, new FuncEqualityComparer<SkinnableInfo>(almostEqual));
|
||||||
|
|
||||||
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
protected override WorkingBeatmap CreateWorkingBeatmap(IBeatmap beatmap, Storyboard storyboard = null)
|
||||||
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
=> new CustomSkinWorkingBeatmap(beatmap, storyboard, Clock, Audio, currentBeatmapSkin);
|
||||||
|
|
||||||
|
@ -0,0 +1,108 @@
|
|||||||
|
// 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.Diagnostics;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Testing;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Screens.Play;
|
||||||
|
using osu.Game.Screens.Play.HUD;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.Gameplay
|
||||||
|
{
|
||||||
|
public class TestScenePerformancePointsCounter : OsuTestScene
|
||||||
|
{
|
||||||
|
[Cached]
|
||||||
|
private GameplayState gameplayState;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private ScoreProcessor scoreProcessor;
|
||||||
|
|
||||||
|
private int iteration;
|
||||||
|
private PerformancePointsCounter counter;
|
||||||
|
|
||||||
|
public TestScenePerformancePointsCounter()
|
||||||
|
{
|
||||||
|
var ruleset = CreateRuleset();
|
||||||
|
|
||||||
|
Debug.Assert(ruleset != null);
|
||||||
|
|
||||||
|
var beatmap = CreateWorkingBeatmap(ruleset.RulesetInfo)
|
||||||
|
.GetPlayableBeatmap(ruleset.RulesetInfo);
|
||||||
|
|
||||||
|
gameplayState = new GameplayState(beatmap, ruleset);
|
||||||
|
scoreProcessor = new ScoreProcessor();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override Ruleset CreateRuleset() => new OsuRuleset();
|
||||||
|
|
||||||
|
[SetUpSteps]
|
||||||
|
public void SetUpSteps()
|
||||||
|
{
|
||||||
|
AddStep("Create counter", () =>
|
||||||
|
{
|
||||||
|
iteration = 0;
|
||||||
|
|
||||||
|
Child = counter = new PerformancePointsCounter
|
||||||
|
{
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Scale = new Vector2(5),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasicCounting()
|
||||||
|
{
|
||||||
|
int previousValue = 0;
|
||||||
|
|
||||||
|
AddAssert("counter displaying zero", () => counter.Current.Value == 0);
|
||||||
|
|
||||||
|
AddRepeatStep("Add judgement", applyOneJudgement, 10);
|
||||||
|
|
||||||
|
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
|
||||||
|
AddUntilStep("counter opaque", () => counter.Child.Alpha == 1);
|
||||||
|
|
||||||
|
AddStep("Revert judgement", () =>
|
||||||
|
{
|
||||||
|
previousValue = counter.Current.Value;
|
||||||
|
|
||||||
|
scoreProcessor.RevertResult(new JudgementResult(new HitObject(), new OsuJudgement()));
|
||||||
|
});
|
||||||
|
|
||||||
|
AddUntilStep("counter decreased", () => counter.Current.Value < previousValue);
|
||||||
|
|
||||||
|
AddStep("Add judgement", applyOneJudgement);
|
||||||
|
|
||||||
|
AddUntilStep("counter non-zero", () => counter.Current.Value > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyOneJudgement()
|
||||||
|
{
|
||||||
|
var scoreInfo = gameplayState.Score.ScoreInfo;
|
||||||
|
|
||||||
|
scoreInfo.MaxCombo = iteration * 1000;
|
||||||
|
scoreInfo.Accuracy = 1;
|
||||||
|
scoreInfo.Statistics[HitResult.Great] = iteration * 1000;
|
||||||
|
|
||||||
|
scoreProcessor.ApplyResult(new OsuJudgementResult(new HitObject
|
||||||
|
{
|
||||||
|
StartTime = iteration * 10000,
|
||||||
|
}, new OsuJudgement())
|
||||||
|
{
|
||||||
|
Type = HitResult.Perfect,
|
||||||
|
});
|
||||||
|
|
||||||
|
iteration++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -17,6 +17,7 @@ using osu.Framework.Utils;
|
|||||||
using osu.Game.Configuration;
|
using osu.Game.Configuration;
|
||||||
using osu.Game.Database;
|
using osu.Game.Database;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.UI;
|
using osu.Game.Rulesets.UI;
|
||||||
|
|
||||||
@ -147,6 +148,14 @@ namespace osu.Game.Beatmaps
|
|||||||
}, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
}, token, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<List<TimedDifficultyAttributes>> GetTimedDifficultyAttributesAsync(WorkingBeatmap beatmap, Ruleset ruleset, Mod[] mods, CancellationToken token = default)
|
||||||
|
{
|
||||||
|
return Task.Factory.StartNew(() => ruleset.CreateDifficultyCalculator(beatmap).CalculateTimed(mods),
|
||||||
|
token,
|
||||||
|
TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously,
|
||||||
|
updateScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves the <see cref="DifficultyRating"/> that describes a star rating.
|
/// Retrieves the <see cref="DifficultyRating"/> that describes a star rating.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -81,7 +81,7 @@ namespace osu.Game.Beatmaps
|
|||||||
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
|
||||||
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
|
||||||
|
|
||||||
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
public virtual IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
||||||
{
|
{
|
||||||
using (var cancellationSource = createCancellationTokenSource(timeout))
|
using (var cancellationSource = createCancellationTokenSource(timeout))
|
||||||
{
|
{
|
||||||
|
@ -25,7 +25,9 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
set => current.Current = value;
|
set => current.Current = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
private SpriteText displayedCountSpriteText;
|
private IHasText displayedCountText;
|
||||||
|
|
||||||
|
public Drawable DrawableCount { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If true, the roll-up duration will be proportional to change in value.
|
/// If true, the roll-up duration will be proportional to change in value.
|
||||||
@ -72,16 +74,16 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
[BackgroundDependencyLoader]
|
[BackgroundDependencyLoader]
|
||||||
private void load()
|
private void load()
|
||||||
{
|
{
|
||||||
displayedCountSpriteText = CreateSpriteText();
|
displayedCountText = CreateText();
|
||||||
|
|
||||||
UpdateDisplay();
|
UpdateDisplay();
|
||||||
Child = displayedCountSpriteText;
|
Child = DrawableCount = (Drawable)displayedCountText;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void UpdateDisplay()
|
protected void UpdateDisplay()
|
||||||
{
|
{
|
||||||
if (displayedCountSpriteText != null)
|
if (displayedCountText != null)
|
||||||
displayedCountSpriteText.Text = FormatCount(DisplayedCount);
|
displayedCountText.Text = FormatCount(DisplayedCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void LoadComplete()
|
protected override void LoadComplete()
|
||||||
@ -160,6 +162,15 @@ namespace osu.Game.Graphics.UserInterface
|
|||||||
this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing);
|
this.TransformTo(nameof(DisplayedCount), newValue, rollingTotalDuration, RollingEasing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates the text. Delegates to <see cref="CreateSpriteText"/> by default.
|
||||||
|
/// </summary>
|
||||||
|
protected virtual IHasText CreateText() => CreateSpriteText();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates an <see cref="OsuSpriteText"/> which may be used to display this counter's text.
|
||||||
|
/// May not be called if <see cref="CreateText"/> is overridden.
|
||||||
|
/// </summary>
|
||||||
protected virtual OsuSpriteText CreateSpriteText() => new OsuSpriteText
|
protected virtual OsuSpriteText CreateSpriteText() => new OsuSpriteText
|
||||||
{
|
{
|
||||||
Font = OsuFont.Numeric.With(size: 40f),
|
Font = OsuFont.Numeric.With(size: 40f),
|
||||||
|
@ -7,6 +7,8 @@ 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.Beatmaps;
|
||||||
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Timing;
|
||||||
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
using osu.Game.Rulesets.Difficulty.Preprocessing;
|
||||||
using osu.Game.Rulesets.Difficulty.Skills;
|
using osu.Game.Rulesets.Difficulty.Skills;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
@ -16,6 +18,14 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
{
|
{
|
||||||
public abstract class DifficultyCalculator
|
public abstract class DifficultyCalculator
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The beatmap for which difficulty will be calculated.
|
||||||
|
/// </summary>
|
||||||
|
protected IBeatmap Beatmap { get; private set; }
|
||||||
|
|
||||||
|
private Mod[] playableMods;
|
||||||
|
private double clockRate;
|
||||||
|
|
||||||
private readonly Ruleset ruleset;
|
private readonly Ruleset ruleset;
|
||||||
private readonly WorkingBeatmap beatmap;
|
private readonly WorkingBeatmap beatmap;
|
||||||
|
|
||||||
@ -32,14 +42,45 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
/// <returns>A structure describing the difficulty of the beatmap.</returns>
|
/// <returns>A structure describing the difficulty of the beatmap.</returns>
|
||||||
public DifficultyAttributes Calculate(params Mod[] mods)
|
public DifficultyAttributes Calculate(params Mod[] mods)
|
||||||
{
|
{
|
||||||
mods = mods.Select(m => m.DeepClone()).ToArray();
|
preProcess(mods);
|
||||||
|
|
||||||
IBeatmap playableBeatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
var skills = CreateSkills(Beatmap, playableMods, clockRate);
|
||||||
|
|
||||||
var track = new TrackVirtual(10000);
|
if (!Beatmap.HitObjects.Any())
|
||||||
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate);
|
||||||
|
|
||||||
return calculate(playableBeatmap, mods, track.Rate);
|
foreach (var hitObject in getDifficultyHitObjects())
|
||||||
|
{
|
||||||
|
foreach (var skill in skills)
|
||||||
|
skill.ProcessInternal(hitObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
return CreateDifficultyAttributes(Beatmap, playableMods, skills, clockRate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TimedDifficultyAttributes> CalculateTimed(params Mod[] mods)
|
||||||
|
{
|
||||||
|
preProcess(mods);
|
||||||
|
|
||||||
|
var attribs = new List<TimedDifficultyAttributes>();
|
||||||
|
|
||||||
|
if (!Beatmap.HitObjects.Any())
|
||||||
|
return attribs;
|
||||||
|
|
||||||
|
var skills = CreateSkills(Beatmap, playableMods, clockRate);
|
||||||
|
var progressiveBeatmap = new ProgressiveCalculationBeatmap(Beatmap);
|
||||||
|
|
||||||
|
foreach (var hitObject in getDifficultyHitObjects())
|
||||||
|
{
|
||||||
|
progressiveBeatmap.HitObjects.Add(hitObject.BaseObject);
|
||||||
|
|
||||||
|
foreach (var skill in skills)
|
||||||
|
skill.ProcessInternal(hitObject);
|
||||||
|
|
||||||
|
attribs.Add(new TimedDifficultyAttributes(hitObject.EndTime, CreateDifficultyAttributes(progressiveBeatmap, playableMods, skills, clockRate)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return attribs;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -57,24 +98,23 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DifficultyAttributes calculate(IBeatmap beatmap, Mod[] mods, double clockRate)
|
/// <summary>
|
||||||
|
/// Retrieves the <see cref="DifficultyHitObject"/>s to calculate against.
|
||||||
|
/// </summary>
|
||||||
|
private IEnumerable<DifficultyHitObject> getDifficultyHitObjects() => SortObjects(CreateDifficultyHitObjects(Beatmap, clockRate));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs required tasks before every calculation.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mods">The original list of <see cref="Mod"/>s.</param>
|
||||||
|
private void preProcess(Mod[] mods)
|
||||||
{
|
{
|
||||||
var skills = CreateSkills(beatmap, mods, clockRate);
|
playableMods = mods.Select(m => m.DeepClone()).ToArray();
|
||||||
|
Beatmap = beatmap.GetPlayableBeatmap(ruleset.RulesetInfo, mods);
|
||||||
|
|
||||||
if (!beatmap.HitObjects.Any())
|
var track = new TrackVirtual(10000);
|
||||||
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
|
mods.OfType<IApplicableToTrack>().ForEach(m => m.ApplyToTrack(track));
|
||||||
|
clockRate = track.Rate;
|
||||||
var difficultyHitObjects = SortObjects(CreateDifficultyHitObjects(beatmap, clockRate)).ToList();
|
|
||||||
|
|
||||||
foreach (var hitObject in difficultyHitObjects)
|
|
||||||
{
|
|
||||||
foreach (var skill in skills)
|
|
||||||
{
|
|
||||||
skill.ProcessInternal(hitObject);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return CreateDifficultyAttributes(beatmap, mods, skills, clockRate);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -86,7 +126,7 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
=> input.OrderBy(h => h.BaseObject.StartTime);
|
=> input.OrderBy(h => h.BaseObject.StartTime);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates all <see cref="Mod"/> combinations which adjust the <see cref="Beatmap"/> difficulty.
|
/// Creates all <see cref="Mod"/> combinations which adjust the <see cref="Beatmaps.Beatmap"/> difficulty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Mod[] CreateDifficultyAdjustmentModCombinations()
|
public Mod[] CreateDifficultyAdjustmentModCombinations()
|
||||||
{
|
{
|
||||||
@ -154,14 +194,15 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieves all <see cref="Mod"/>s which adjust the <see cref="Beatmap"/> difficulty.
|
/// Retrieves all <see cref="Mod"/>s which adjust the <see cref="Beatmaps.Beatmap"/> difficulty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty<Mod>();
|
protected virtual Mod[] DifficultyAdjustmentMods => Array.Empty<Mod>();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates <see cref="DifficultyAttributes"/> to describe beatmap's calculated difficulty.
|
/// Creates <see cref="DifficultyAttributes"/> to describe beatmap's calculated difficulty.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was calculated.</param>
|
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty was calculated.
|
||||||
|
/// This may differ from <see cref="Beatmap"/> in the case of timed calculation.</param>
|
||||||
/// <param name="mods">The <see cref="Mod"/>s that difficulty was calculated with.</param>
|
/// <param name="mods">The <see cref="Mod"/>s that difficulty was calculated with.</param>
|
||||||
/// <param name="skills">The skills which processed the beatmap.</param>
|
/// <param name="skills">The skills which processed the beatmap.</param>
|
||||||
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
/// <param name="clockRate">The rate at which the gameplay clock is run at.</param>
|
||||||
@ -178,10 +219,51 @@ namespace osu.Game.Rulesets.Difficulty
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the <see cref="Skill"/>s to calculate the difficulty of an <see cref="IBeatmap"/>.
|
/// Creates the <see cref="Skill"/>s to calculate the difficulty of an <see cref="IBeatmap"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.</param>
|
/// <param name="beatmap">The <see cref="IBeatmap"/> whose difficulty will be calculated.
|
||||||
|
/// This may differ from <see cref="Beatmap"/> in the case of timed calculation.</param>
|
||||||
/// <param name="mods">Mods to calculate difficulty with.</param>
|
/// <param name="mods">Mods to calculate difficulty with.</param>
|
||||||
/// <param name="clockRate">Clockrate to calculate difficulty with.</param>
|
/// <param name="clockRate">Clockrate to calculate difficulty with.</param>
|
||||||
/// <returns>The <see cref="Skill"/>s.</returns>
|
/// <returns>The <see cref="Skill"/>s.</returns>
|
||||||
protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate);
|
protected abstract Skill[] CreateSkills(IBeatmap beatmap, Mod[] mods, double clockRate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to calculate timed difficulty attributes, where only a subset of hitobjects should be visible at any point in time.
|
||||||
|
/// </summary>
|
||||||
|
private class ProgressiveCalculationBeatmap : IBeatmap
|
||||||
|
{
|
||||||
|
private readonly IBeatmap baseBeatmap;
|
||||||
|
|
||||||
|
public ProgressiveCalculationBeatmap(IBeatmap baseBeatmap)
|
||||||
|
{
|
||||||
|
this.baseBeatmap = baseBeatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly List<HitObject> HitObjects = new List<HitObject>();
|
||||||
|
|
||||||
|
IReadOnlyList<HitObject> IBeatmap.HitObjects => HitObjects;
|
||||||
|
|
||||||
|
#region Delegated IBeatmap implementation
|
||||||
|
|
||||||
|
public BeatmapInfo BeatmapInfo
|
||||||
|
{
|
||||||
|
get => baseBeatmap.BeatmapInfo;
|
||||||
|
set => baseBeatmap.BeatmapInfo = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ControlPointInfo ControlPointInfo
|
||||||
|
{
|
||||||
|
get => baseBeatmap.ControlPointInfo;
|
||||||
|
set => baseBeatmap.ControlPointInfo = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public BeatmapMetadata Metadata => baseBeatmap.Metadata;
|
||||||
|
public List<BreakPeriod> Breaks => baseBeatmap.Breaks;
|
||||||
|
public double TotalBreakTime => baseBeatmap.TotalBreakTime;
|
||||||
|
public IEnumerable<BeatmapStatistic> GetStatistics() => baseBeatmap.GetStatistics();
|
||||||
|
public double GetMostCommonBeatLength() => baseBeatmap.GetMostCommonBeatLength();
|
||||||
|
public IBeatmap Clone() => new ProgressiveCalculationBeatmap(baseBeatmap.Clone());
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
25
osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs
Normal file
25
osu.Game/Rulesets/Difficulty/TimedDifficultyAttributes.cs
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// 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;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Difficulty
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Wraps a <see cref="DifficultyAttributes"/> object and adds a time value for which the attribute is valid.
|
||||||
|
/// Output by <see cref="DifficultyCalculator.CalculateTimed"/>.
|
||||||
|
/// </summary>
|
||||||
|
public class TimedDifficultyAttributes : IComparable<TimedDifficultyAttributes>
|
||||||
|
{
|
||||||
|
public readonly double Time;
|
||||||
|
public readonly DifficultyAttributes Attributes;
|
||||||
|
|
||||||
|
public TimedDifficultyAttributes(double time, DifficultyAttributes attributes)
|
||||||
|
{
|
||||||
|
Time = time;
|
||||||
|
Attributes = attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo(TimedDifficultyAttributes other) => Time.CompareTo(other.Time);
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,11 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event Action<JudgementResult> NewJudgement;
|
public event Action<JudgementResult> NewJudgement;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when a judgement is reverted, usually due to rewinding gameplay.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<JudgementResult> JudgementReverted;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The maximum number of hits that can be judged.
|
/// The maximum number of hits that can be judged.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -71,6 +76,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
JudgedHits--;
|
JudgedHits--;
|
||||||
|
|
||||||
RevertResultInternal(result);
|
RevertResultInternal(result);
|
||||||
|
|
||||||
|
JudgementReverted?.Invoke(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
@ -1,12 +1,14 @@
|
|||||||
// 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 osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets;
|
using osu.Game.Rulesets;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
#nullable enable
|
#nullable enable
|
||||||
|
|
||||||
@ -30,7 +32,12 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The mods applied to the gameplay.
|
/// The mods applied to the gameplay.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public IReadOnlyList<Mod> Mods;
|
public readonly IReadOnlyList<Mod> Mods;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The gameplay score.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Score Score;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A bindable tracking the last judgement result applied to any hit object.
|
/// A bindable tracking the last judgement result applied to any hit object.
|
||||||
@ -39,11 +46,12 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
|
private readonly Bindable<JudgementResult> lastJudgementResult = new Bindable<JudgementResult>();
|
||||||
|
|
||||||
public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList<Mod> mods)
|
public GameplayState(IBeatmap beatmap, Ruleset ruleset, IReadOnlyList<Mod>? mods = null, Score? score = null)
|
||||||
{
|
{
|
||||||
Beatmap = beatmap;
|
Beatmap = beatmap;
|
||||||
Ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
Mods = mods;
|
Score = score ?? new Score();
|
||||||
|
Mods = mods ?? ArraySegment<Mod>.Empty;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
219
osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs
Normal file
219
osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs
Normal file
@ -0,0 +1,219 @@
|
|||||||
|
// 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;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Audio.Track;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Framework.Graphics.Sprites;
|
||||||
|
using osu.Framework.Graphics.Textures;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics;
|
||||||
|
using osu.Game.Graphics.Sprites;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Rulesets.Difficulty;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Rulesets.Mods;
|
||||||
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Skinning;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.Play.HUD
|
||||||
|
{
|
||||||
|
public class PerformancePointsCounter : RollingCounter<int>, ISkinnableDrawable
|
||||||
|
{
|
||||||
|
public bool UsesFixedAnchor { get; set; }
|
||||||
|
|
||||||
|
protected override bool IsRollingProportional => true;
|
||||||
|
|
||||||
|
protected override double RollingDuration => 1000;
|
||||||
|
|
||||||
|
private const float alpha_when_invalid = 0.3f;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private ScoreProcessor scoreProcessor { get; set; }
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
[CanBeNull]
|
||||||
|
private GameplayState gameplayState { get; set; }
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private List<TimedDifficultyAttributes> timedAttributes;
|
||||||
|
|
||||||
|
private readonly CancellationTokenSource loadCancellationSource = new CancellationTokenSource();
|
||||||
|
|
||||||
|
private JudgementResult lastJudgement;
|
||||||
|
|
||||||
|
public PerformancePointsCounter()
|
||||||
|
{
|
||||||
|
Current.Value = DisplayedCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load(OsuColour colours, BeatmapDifficultyCache difficultyCache)
|
||||||
|
{
|
||||||
|
Colour = colours.BlueLighter;
|
||||||
|
|
||||||
|
if (gameplayState != null)
|
||||||
|
{
|
||||||
|
var gameplayWorkingBeatmap = new GameplayWorkingBeatmap(gameplayState.Beatmap);
|
||||||
|
difficultyCache.GetTimedDifficultyAttributesAsync(gameplayWorkingBeatmap, gameplayState.Ruleset, gameplayState.Mods.ToArray(), loadCancellationSource.Token)
|
||||||
|
.ContinueWith(r => Schedule(() =>
|
||||||
|
{
|
||||||
|
timedAttributes = r.Result;
|
||||||
|
IsValid = true;
|
||||||
|
if (lastJudgement != null)
|
||||||
|
onJudgementChanged(lastJudgement);
|
||||||
|
}), TaskContinuationOptions.OnlyOnRanToCompletion);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
|
||||||
|
if (scoreProcessor != null)
|
||||||
|
{
|
||||||
|
scoreProcessor.NewJudgement += onJudgementChanged;
|
||||||
|
scoreProcessor.JudgementReverted += onJudgementChanged;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool isValid;
|
||||||
|
|
||||||
|
protected bool IsValid
|
||||||
|
{
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (value == isValid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
isValid = value;
|
||||||
|
DrawableCount.FadeTo(isValid ? 1 : alpha_when_invalid, 1000, Easing.OutQuint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onJudgementChanged(JudgementResult judgement)
|
||||||
|
{
|
||||||
|
lastJudgement = judgement;
|
||||||
|
|
||||||
|
var attrib = getAttributeAtTime(judgement);
|
||||||
|
|
||||||
|
if (gameplayState == null || attrib == null)
|
||||||
|
{
|
||||||
|
IsValid = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var calculator = gameplayState.Ruleset.CreatePerformanceCalculator(attrib, gameplayState.Score.ScoreInfo);
|
||||||
|
|
||||||
|
Current.Value = (int)Math.Round(calculator?.Calculate() ?? 0, MidpointRounding.AwayFromZero);
|
||||||
|
IsValid = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
private DifficultyAttributes getAttributeAtTime(JudgementResult judgement)
|
||||||
|
{
|
||||||
|
if (timedAttributes == null || timedAttributes.Count == 0)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
int attribIndex = timedAttributes.BinarySearch(new TimedDifficultyAttributes(judgement.HitObject.GetEndTime(), null));
|
||||||
|
if (attribIndex < 0)
|
||||||
|
attribIndex = ~attribIndex - 1;
|
||||||
|
|
||||||
|
return timedAttributes[Math.Clamp(attribIndex, 0, timedAttributes.Count - 1)].Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override LocalisableString FormatCount(int count) => count.ToString(@"D");
|
||||||
|
|
||||||
|
protected override IHasText CreateText() => new TextComponent
|
||||||
|
{
|
||||||
|
Alpha = alpha_when_invalid
|
||||||
|
};
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
|
||||||
|
if (scoreProcessor != null)
|
||||||
|
scoreProcessor.NewJudgement -= onJudgementChanged;
|
||||||
|
|
||||||
|
loadCancellationSource?.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class TextComponent : CompositeDrawable, IHasText
|
||||||
|
{
|
||||||
|
public LocalisableString Text
|
||||||
|
{
|
||||||
|
get => text.Text;
|
||||||
|
set => text.Text = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly OsuSpriteText text;
|
||||||
|
|
||||||
|
public TextComponent()
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both;
|
||||||
|
|
||||||
|
InternalChild = new FillFlowContainer
|
||||||
|
{
|
||||||
|
AutoSizeAxes = Axes.Both,
|
||||||
|
Spacing = new Vector2(2),
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
text = new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Font = OsuFont.Numeric.With(size: 16)
|
||||||
|
},
|
||||||
|
new OsuSpriteText
|
||||||
|
{
|
||||||
|
Anchor = Anchor.BottomLeft,
|
||||||
|
Origin = Anchor.BottomLeft,
|
||||||
|
Text = @"pp",
|
||||||
|
Font = OsuFont.Numeric.With(size: 8),
|
||||||
|
Padding = new MarginPadding { Bottom = 1.5f }, // align baseline better
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: This class shouldn't exist, but requires breaking changes to allow DifficultyCalculator to receive an IBeatmap.
|
||||||
|
private class GameplayWorkingBeatmap : WorkingBeatmap
|
||||||
|
{
|
||||||
|
private readonly IBeatmap gameplayBeatmap;
|
||||||
|
|
||||||
|
public GameplayWorkingBeatmap(IBeatmap gameplayBeatmap)
|
||||||
|
: base(gameplayBeatmap.BeatmapInfo, null)
|
||||||
|
{
|
||||||
|
this.gameplayBeatmap = gameplayBeatmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
|
||||||
|
=> gameplayBeatmap;
|
||||||
|
|
||||||
|
protected override IBeatmap GetBeatmap() => gameplayBeatmap;
|
||||||
|
|
||||||
|
protected override Texture GetBackground() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
protected override Track GetBeatmapTrack() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
protected internal override ISkin GetSkin() => throw new NotImplementedException();
|
||||||
|
|
||||||
|
public override Stream GetStream(string storagePath) => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -161,13 +161,6 @@ namespace osu.Game.Screens.Play
|
|||||||
if (!LoadedBeatmapSuccessfully)
|
if (!LoadedBeatmapSuccessfully)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Score = CreateScore();
|
|
||||||
|
|
||||||
// ensure the score is in a consistent state with the current player.
|
|
||||||
Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo;
|
|
||||||
Score.ScoreInfo.Ruleset = ruleset.RulesetInfo;
|
|
||||||
Score.ScoreInfo.Mods = Mods.Value.ToArray();
|
|
||||||
|
|
||||||
PrepareReplay();
|
PrepareReplay();
|
||||||
|
|
||||||
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||||
@ -225,7 +218,14 @@ namespace osu.Game.Screens.Play
|
|||||||
|
|
||||||
InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
|
InternalChild = GameplayClockContainer = CreateGameplayClockContainer(Beatmap.Value, DrawableRuleset.GameplayStartTime);
|
||||||
|
|
||||||
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value));
|
Score = CreateScore(playableBeatmap);
|
||||||
|
|
||||||
|
// ensure the score is in a consistent state with the current player.
|
||||||
|
Score.ScoreInfo.BeatmapInfo = Beatmap.Value.BeatmapInfo;
|
||||||
|
Score.ScoreInfo.Ruleset = ruleset.RulesetInfo;
|
||||||
|
Score.ScoreInfo.Mods = Mods.Value.ToArray();
|
||||||
|
|
||||||
|
dependencies.CacheAs(GameplayState = new GameplayState(playableBeatmap, ruleset, Mods.Value, Score));
|
||||||
|
|
||||||
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
|
AddInternal(screenSuspension = new ScreenSuspensionHandler(GameplayClockContainer));
|
||||||
|
|
||||||
@ -988,8 +988,9 @@ namespace osu.Game.Screens.Play
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the player's <see cref="Scoring.Score"/>.
|
/// Creates the player's <see cref="Scoring.Score"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <param name="beatmap"></param>
|
||||||
/// <returns>The <see cref="Scoring.Score"/>.</returns>
|
/// <returns>The <see cref="Scoring.Score"/>.</returns>
|
||||||
protected virtual Score CreateScore() => new Score
|
protected virtual Score CreateScore(IBeatmap beatmap) => new Score
|
||||||
{
|
{
|
||||||
ScoreInfo = new ScoreInfo { User = api.LocalUser.Value },
|
ScoreInfo = new ScoreInfo { User = api.LocalUser.Value },
|
||||||
};
|
};
|
||||||
|
@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play
|
|||||||
DrawableRuleset?.SetReplayScore(Score);
|
DrawableRuleset?.SetReplayScore(Score);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Score CreateScore() => createScore(GameplayState.Beatmap, Mods.Value);
|
protected override Score CreateScore(IBeatmap beatmap) => createScore(beatmap, Mods.Value);
|
||||||
|
|
||||||
// Don't re-import replay scores as they're already present in the database.
|
// Don't re-import replay scores as they're already present in the database.
|
||||||
protected override Task ImportScore(Score score) => Task.CompletedTask;
|
protected override Task ImportScore(Score score) => Task.CompletedTask;
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
using osu.Framework.Allocation;
|
using osu.Framework.Allocation;
|
||||||
using osu.Framework.Graphics;
|
using osu.Framework.Graphics;
|
||||||
using osu.Framework.Screens;
|
using osu.Framework.Screens;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Graphics;
|
using osu.Game.Graphics;
|
||||||
using osu.Game.Graphics.Sprites;
|
using osu.Game.Graphics.Sprites;
|
||||||
using osu.Game.Online.Spectator;
|
using osu.Game.Online.Spectator;
|
||||||
@ -79,7 +80,7 @@ namespace osu.Game.Screens.Play
|
|||||||
NonFrameStableSeek(score.Replay.Frames[0].Time);
|
NonFrameStableSeek(score.Replay.Frames[0].Time);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override Score CreateScore() => score;
|
protected override Score CreateScore(IBeatmap beatmap) => score;
|
||||||
|
|
||||||
protected override ResultsScreen CreateResults(ScoreInfo score)
|
protected override ResultsScreen CreateResults(ScoreInfo score)
|
||||||
=> new SpectatorResultsScreen(score);
|
=> new SpectatorResultsScreen(score);
|
||||||
|
@ -68,6 +68,7 @@ namespace osu.Game.Skinning
|
|||||||
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
var score = container.OfType<DefaultScoreCounter>().FirstOrDefault();
|
||||||
var accuracy = container.OfType<DefaultAccuracyCounter>().FirstOrDefault();
|
var accuracy = container.OfType<DefaultAccuracyCounter>().FirstOrDefault();
|
||||||
var combo = container.OfType<DefaultComboCounter>().FirstOrDefault();
|
var combo = container.OfType<DefaultComboCounter>().FirstOrDefault();
|
||||||
|
var ppCounter = container.OfType<PerformancePointsCounter>().FirstOrDefault();
|
||||||
|
|
||||||
if (score != null)
|
if (score != null)
|
||||||
{
|
{
|
||||||
@ -81,6 +82,13 @@ namespace osu.Game.Skinning
|
|||||||
|
|
||||||
score.Position = new Vector2(0, vertical_offset);
|
score.Position = new Vector2(0, vertical_offset);
|
||||||
|
|
||||||
|
if (ppCounter != null)
|
||||||
|
{
|
||||||
|
ppCounter.Y = score.Position.Y + ppCounter.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).Y - 4;
|
||||||
|
ppCounter.Origin = Anchor.TopCentre;
|
||||||
|
ppCounter.Anchor = Anchor.TopCentre;
|
||||||
|
}
|
||||||
|
|
||||||
if (accuracy != null)
|
if (accuracy != null)
|
||||||
{
|
{
|
||||||
accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5);
|
accuracy.Position = new Vector2(-accuracy.ScreenSpaceDeltaToParentSpace(score.ScreenSpaceDrawQuad.Size).X / 2 - horizontal_padding, vertical_offset + 5);
|
||||||
@ -123,6 +131,7 @@ namespace osu.Game.Skinning
|
|||||||
new SongProgress(),
|
new SongProgress(),
|
||||||
new BarHitErrorMeter(),
|
new BarHitErrorMeter(),
|
||||||
new BarHitErrorMeter(),
|
new BarHitErrorMeter(),
|
||||||
|
new PerformancePointsCounter()
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user