// Copyright (c) ppy Pty Ltd . 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.Linq; using NUnit.Framework; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Tests.Visual; using osuTK; namespace osu.Game.Rulesets.Osu.Tests { public partial class TestSceneSpinnerJudgement : RateAdjustedBeatmapTestScene { private const double time_spinner_start = 2000; private const double time_spinner_end = 4000; private List judgementResults = new List(); private ScoreAccessibleReplayPlayer currentPlayer = null!; [Test] public void TestHitNothing() { performTest(new List()); AddAssert("all min judgements", () => judgementResults.All(result => result.Type == result.Judgement.MinResult)); } [TestCase(1)] [TestCase(2)] [TestCase(5)] public void TestNumberOfSpins(int spins) { performTest(generateReplay(spins)); for (int i = 0; i < spins; ++i) assertResult(i, HitResult.SmallBonus); assertResult(spins, HitResult.IgnoreMiss); } [Test] public void TestHitEverything() { performTest(generateReplay(20)); AddAssert("all max judgements", () => judgementResults.All(result => result.Type == result.Judgement.MaxResult)); } private static List generateReplay(int spins) { var replayFrames = new List(); const int frames_per_spin = 30; for (int i = 0; i < spins * frames_per_spin; ++i) { float totalProgress = i / (float)(spins * frames_per_spin); float spinProgress = (i % frames_per_spin) / (float)frames_per_spin; double time = time_spinner_start + (time_spinner_end - time_spinner_start) * totalProgress; float posX = MathF.Cos(2 * MathF.PI * spinProgress); float posY = MathF.Sin(2 * MathF.PI * spinProgress); Vector2 finalPos = OsuPlayfield.BASE_SIZE / 2 + new Vector2(posX, posY) * 50; replayFrames.Add(new OsuReplayFrame(time, finalPos, OsuAction.LeftButton)); } return replayFrames; } private void performTest(List frames) { AddStep("load player", () => { Beatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = { new Spinner { StartTime = time_spinner_start, EndTime = time_spinner_end, Position = OsuPlayfield.BASE_SIZE / 2 } }, BeatmapInfo = { Difficulty = new BeatmapDifficulty(), Ruleset = new OsuRuleset().RulesetInfo }, }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); p.OnLoadComplete += _ => { p.ScoreProcessor.NewJudgement += result => { if (currentPlayer == p) judgementResults.Add(result); }; }; LoadScreen(currentPlayer = p); judgementResults = new List(); }); AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); } private void assertResult(int index, HitResult expectedResult) { AddAssert($"{typeof(T).ReadableName()} ({index}) judged as {expectedResult}", () => judgementResults.Where(j => j.HitObject is T).OrderBy(j => j.HitObject.StartTime).ElementAt(index).Type, () => Is.EqualTo(expectedResult)); } private partial class ScoreAccessibleReplayPlayer : ReplayPlayer { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) : base(score, new PlayerConfiguration { AllowPause = false, ShowResults = false, }) { } } } }