mirror of
https://github.com/osukey/osukey.git
synced 2025-07-01 16:29:58 +09:00
Reset ScoreProcessor from statistics replay frames
This commit is contained in:
@ -1,11 +1,17 @@
|
|||||||
// 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 NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using osu.Framework.Testing;
|
using osu.Framework.Testing;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Online.Spectator;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu;
|
||||||
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
using osu.Game.Rulesets.Osu.Replays;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Tests.Visual;
|
using osu.Game.Tests.Visual;
|
||||||
|
|
||||||
@ -42,6 +48,37 @@ namespace osu.Game.Tests.Gameplay
|
|||||||
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestResetFromReplayFrame()
|
||||||
|
{
|
||||||
|
var beatmap = new Beatmap<HitObject> { HitObjects = { new HitCircle() } };
|
||||||
|
|
||||||
|
var scoreProcessor = new ScoreProcessor();
|
||||||
|
scoreProcessor.ApplyBeatmap(beatmap);
|
||||||
|
|
||||||
|
scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TestJudgement(HitResult.Great)) { Type = HitResult.Great });
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(1_000_000));
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
|
||||||
|
// Reset with a miss instead.
|
||||||
|
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame
|
||||||
|
{
|
||||||
|
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int> { { HitResult.Miss, 1 } }, DateTimeOffset.Now)
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.EqualTo(1));
|
||||||
|
|
||||||
|
// Reset with no judged hit.
|
||||||
|
scoreProcessor.ResetFromReplayFrame(new OsuRuleset(), new OsuReplayFrame
|
||||||
|
{
|
||||||
|
Header = new FrameHeader(0, 0, 0, new Dictionary<HitResult, int>(), DateTimeOffset.Now)
|
||||||
|
});
|
||||||
|
|
||||||
|
Assert.That(scoreProcessor.TotalScore.Value, Is.Zero);
|
||||||
|
Assert.That(scoreProcessor.JudgedHits, Is.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
private class TestJudgement : Judgement
|
private class TestJudgement : Judgement
|
||||||
{
|
{
|
||||||
public override HitResult MaxResult { get; }
|
public override HitResult MaxResult { get; }
|
||||||
|
@ -8,6 +8,7 @@ using osu.Framework.Graphics;
|
|||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
{
|
{
|
||||||
@ -107,6 +108,17 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
JudgedHits = 0;
|
JudgedHits = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public virtual void ResetFromReplayFrame(Ruleset ruleset, ReplayFrame frame)
|
||||||
|
{
|
||||||
|
if (frame.Header == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
JudgedHits = 0;
|
||||||
|
|
||||||
|
foreach ((_, int count) in frame.Header.Statistics)
|
||||||
|
JudgedHits += count;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
/// Creates the <see cref="JudgementResult"/> that represents the scoring result for a <see cref="HitObject"/>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -7,9 +7,11 @@ using System.Diagnostics;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Framework.Utils;
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Extensions;
|
||||||
using osu.Game.Rulesets.Judgements;
|
using osu.Game.Rulesets.Judgements;
|
||||||
using osu.Game.Rulesets.Mods;
|
using osu.Game.Rulesets.Mods;
|
||||||
using osu.Game.Rulesets.Objects;
|
using osu.Game.Rulesets.Objects;
|
||||||
|
using osu.Game.Rulesets.Replays;
|
||||||
using osu.Game.Scoring;
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Scoring
|
namespace osu.Game.Rulesets.Scoring
|
||||||
@ -18,6 +20,11 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
{
|
{
|
||||||
private const double max_score = 1000000;
|
private const double max_score = 1000000;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Invoked when this <see cref="ScoreProcessor"/> was reset from a replay frame.
|
||||||
|
/// </summary>
|
||||||
|
public event Action OnResetFromReplayFrame;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The current total score.
|
/// The current total score.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -329,12 +336,6 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
HighestCombo.Value = 0;
|
HighestCombo.Value = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool isDisposing)
|
|
||||||
{
|
|
||||||
base.Dispose(isDisposing);
|
|
||||||
hitEvents.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Retrieve a score populated with data for the current play this processor is responsible for.
|
/// Retrieve a score populated with data for the current play this processor is responsible for.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -351,6 +352,70 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
|
|
||||||
score.HitEvents = hitEvents;
|
score.HitEvents = hitEvents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum <see cref="HitResult"/> for a normal hit (i.e. not tick/bonus) for this ruleset. Only populated via <see cref="ResetFromReplayFrame"/>.
|
||||||
|
/// </summary>
|
||||||
|
private HitResult? maxNormalResult;
|
||||||
|
|
||||||
|
public override void ResetFromReplayFrame(Ruleset ruleset, ReplayFrame frame)
|
||||||
|
{
|
||||||
|
base.ResetFromReplayFrame(ruleset, frame);
|
||||||
|
|
||||||
|
if (frame.Header == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
baseScore = 0;
|
||||||
|
rollingMaxBaseScore = 0;
|
||||||
|
HighestCombo.Value = frame.Header.MaxCombo;
|
||||||
|
|
||||||
|
foreach ((HitResult result, int count) in frame.Header.Statistics)
|
||||||
|
{
|
||||||
|
// Bonus scores are counted separately directly from the statistics dictionary later on.
|
||||||
|
if (!result.IsScorable() || result.IsBonus())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// The maximum result of this judgement if it wasn't a miss.
|
||||||
|
// E.g. For a GOOD judgement, the max result is either GREAT/PERFECT depending on which one the ruleset uses (osu!: GREAT, osu!mania: PERFECT).
|
||||||
|
HitResult maxResult;
|
||||||
|
|
||||||
|
switch (result)
|
||||||
|
{
|
||||||
|
case HitResult.LargeTickHit:
|
||||||
|
case HitResult.LargeTickMiss:
|
||||||
|
maxResult = HitResult.LargeTickHit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HitResult.SmallTickHit:
|
||||||
|
case HitResult.SmallTickMiss:
|
||||||
|
maxResult = HitResult.SmallTickHit;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
maxResult = maxNormalResult ??= ruleset.GetHitResults().OrderByDescending(kvp => Judgement.ToNumericResult(kvp.result)).First().result;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
baseScore += Judgement.ToNumericResult(result);
|
||||||
|
rollingMaxBaseScore += Judgement.ToNumericResult(maxResult);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scoreResultCounts.Clear();
|
||||||
|
scoreResultCounts.AddRange(frame.Header.Statistics);
|
||||||
|
|
||||||
|
updateScore();
|
||||||
|
|
||||||
|
OnResetFromReplayFrame?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool isDisposing)
|
||||||
|
{
|
||||||
|
base.Dispose(isDisposing);
|
||||||
|
hitEvents.Clear();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ScoringMode
|
public enum ScoringMode
|
||||||
|
@ -16,6 +16,7 @@ using osu.Game.Configuration;
|
|||||||
using osu.Game.Input;
|
using osu.Game.Input;
|
||||||
using osu.Game.Input.Bindings;
|
using osu.Game.Input.Bindings;
|
||||||
using osu.Game.Input.Handlers;
|
using osu.Game.Input.Handlers;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
using osu.Game.Screens.Play;
|
using osu.Game.Screens.Play;
|
||||||
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
using static osu.Game.Input.Handlers.ReplayInputHandler;
|
||||||
|
|
||||||
@ -24,6 +25,11 @@ namespace osu.Game.Rulesets.UI
|
|||||||
public abstract class RulesetInputManager<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler, IHasRecordingHandler
|
public abstract class RulesetInputManager<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler, IHasRecordingHandler
|
||||||
where T : struct
|
where T : struct
|
||||||
{
|
{
|
||||||
|
private readonly Ruleset ruleset;
|
||||||
|
|
||||||
|
[Resolved(CanBeNull = true)]
|
||||||
|
private ScoreProcessor scoreProcessor { get; set; }
|
||||||
|
|
||||||
private ReplayRecorder recorder;
|
private ReplayRecorder recorder;
|
||||||
|
|
||||||
public ReplayRecorder Recorder
|
public ReplayRecorder Recorder
|
||||||
@ -51,6 +57,8 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
protected RulesetInputManager(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
|
||||||
{
|
{
|
||||||
|
this.ruleset = ruleset.CreateInstance();
|
||||||
|
|
||||||
InternalChild = KeyBindingContainer =
|
InternalChild = KeyBindingContainer =
|
||||||
CreateKeyBindingContainer(ruleset, variant, unique)
|
CreateKeyBindingContainer(ruleset, variant, unique)
|
||||||
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
.WithChild(content = new Container { RelativeSizeAxes = Axes.Both });
|
||||||
@ -66,17 +74,23 @@ namespace osu.Game.Rulesets.UI
|
|||||||
|
|
||||||
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
|
public override void HandleInputStateChange(InputStateChangeEvent inputStateChange)
|
||||||
{
|
{
|
||||||
if (inputStateChange is ReplayStateChangeEvent<T> replayStateChanged)
|
switch (inputStateChange)
|
||||||
{
|
{
|
||||||
foreach (var action in replayStateChanged.ReleasedActions)
|
case ReplayStateChangeEvent<T> stateChangeEvent:
|
||||||
KeyBindingContainer.TriggerReleased(action);
|
foreach (var action in stateChangeEvent.ReleasedActions)
|
||||||
|
KeyBindingContainer.TriggerReleased(action);
|
||||||
|
|
||||||
foreach (var action in replayStateChanged.PressedActions)
|
foreach (var action in stateChangeEvent.PressedActions)
|
||||||
KeyBindingContainer.TriggerPressed(action);
|
KeyBindingContainer.TriggerPressed(action);
|
||||||
}
|
break;
|
||||||
else
|
|
||||||
{
|
case ReplayStatisticsFrameEvent statisticsStateChangeEvent:
|
||||||
base.HandleInputStateChange(inputStateChange);
|
scoreProcessor?.ResetFromReplayFrame(ruleset, statisticsStateChangeEvent.Frame);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
base.HandleInputStateChange(inputStateChange);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,6 +165,7 @@ namespace osu.Game.Screens.Play
|
|||||||
PrepareReplay();
|
PrepareReplay();
|
||||||
|
|
||||||
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
ScoreProcessor.NewJudgement += result => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||||
|
ScoreProcessor.OnResetFromReplayFrame += () => ScoreProcessor.PopulateScore(Score.ScoreInfo);
|
||||||
|
|
||||||
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
gameActive.BindValueChanged(_ => updatePauseOnFocusLostState(), true);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user