// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Screens; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; 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.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 class TestSceneNoteLock : RateAdjustedBeatmapTestScene { private const double time_first_circle = 1500; private const double time_second_circle = 1600; private static readonly Vector2 position_first_circle = Vector2.Zero; private static readonly Vector2 position_second_circle = new Vector2(80); /// /// Tests clicking the second circle before the first hitobject's start time, while the first hitobject HAS NOT been judged. /// [Test] public void TestClickSecondCircleBeforeFirstCircleTime() { performTest(new List { new OsuReplayFrame { Time = time_first_circle - 100, Position = position_second_circle, Actions = { OsuAction.LeftButton } } }); addJudgementAssert(HitResult.Miss, HitResult.Miss); } /// /// Tests clicking the second circle at the first hitobject's start time, while the first hitobject HAS NOT been judged. /// [Test] public void TestClickSecondCircleAtFirstCircleTime() { performTest(new List { new OsuReplayFrame { Time = time_first_circle, Position = position_second_circle, Actions = { OsuAction.LeftButton } } }); addJudgementAssert(HitResult.Miss, HitResult.Miss); } /// /// Tests clicking the second circle after the first hitobject's start time, while the first hitobject HAS NOT been judged. /// [Test] public void TestClickSecondCircleAfterFirstCircleTime() { performTest(new List { new OsuReplayFrame { Time = time_first_circle + 100, Position = position_second_circle, Actions = { OsuAction.LeftButton } } }); addJudgementAssert(HitResult.Miss, HitResult.Great); } /// /// Tests clicking the second circle before the first hitobject's start time, while the first hitobject HAS been judged. /// [Test] public void TestClickSecondCircleBeforeFirstCircleTimeWithFirstCircleJudged() { performTest(new List { new OsuReplayFrame { Time = time_first_circle - 200, Position = position_first_circle, Actions = { OsuAction.LeftButton } }, new OsuReplayFrame { Time = time_first_circle - 100, Position = position_second_circle, Actions = { OsuAction.RightButton } } }); addJudgementAssert(HitResult.Great, HitResult.Great); } private void addJudgementAssert(HitResult firstCircle, HitResult secondCircle) { AddAssert($"first circle judgement is {firstCircle}", () => judgementResults.Single(r => r.HitObject.StartTime == time_first_circle).Type == firstCircle); AddAssert($"second circle judgement is {secondCircle}", () => judgementResults.Single(r => r.HitObject.StartTime == time_second_circle).Type == secondCircle); } private ScoreAccessibleReplayPlayer currentPlayer; private List judgementResults; private bool allJudgedFired; private void performTest(List frames) { AddStep("load player", () => { Beatmap.Value = CreateWorkingBeatmap(new Beatmap { HitObjects = { new TestHitCircle { StartTime = time_first_circle, Position = position_first_circle }, new TestHitCircle { StartTime = time_second_circle, Position = position_second_circle } }, BeatmapInfo = { BaseDifficulty = new BeatmapDifficulty { SliderTickRate = 3 }, Ruleset = new OsuRuleset().RulesetInfo }, }); Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); p.OnLoadComplete += _ => { p.ScoreProcessor.NewJudgement += result => { if (currentPlayer == p) judgementResults.Add(result); }; p.ScoreProcessor.AllJudged += () => { if (currentPlayer == p) allJudgedFired = true; }; }; LoadScreen(currentPlayer = p); allJudgedFired = false; judgementResults = new List(); }); AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); AddUntilStep("Wait for all judged", () => allJudgedFired); } private class TestHitCircle : HitCircle { protected override HitWindows CreateHitWindows() => new TestHitWindows(); } private class TestHitWindows : HitWindows { private static readonly DifficultyRange[] ranges = { new DifficultyRange(HitResult.Great, 500, 500, 500), new DifficultyRange(HitResult.Miss, 1000, 1000, 1000), }; public override bool IsHitResultAllowed(HitResult result) => result == HitResult.Great || result == HitResult.Miss; protected override DifficultyRange[] GetRanges() => ranges; } private class ScoreAccessibleReplayPlayer : ReplayPlayer { public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; protected override bool PauseOnFocusLost => false; public ScoreAccessibleReplayPlayer(Score score) : base(score, false, false) { } } } }