From 8a5da6fb192bc5bc971ce715d06a57186a4b43d9 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Oct 2020 12:38:34 +0200 Subject: [PATCH 01/13] Add proper Autoplay generation --- .../Mods/SentakkiModAutoTouch.cs | 5 +- .../Mods/SentakkiModAutoplay.cs | 8 +- .../Replays/SentakkiAutoGenerator.cs | 90 ++++++++++++++++++- 3 files changed, 93 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoTouch.cs b/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoTouch.cs index c445ab09f..ef03457f3 100644 --- a/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoTouch.cs +++ b/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoTouch.cs @@ -22,9 +22,8 @@ public class SentakkiModAutoTouch : Mod, IApplicableToDrawableHitObjects public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var d in drawables.OfType()) - if (d is DrawableSentakkiTouchHitObject t) - t.AutoTouch = true; + foreach (var d in drawables.OfType()) + d.AutoTouch = true; } } } diff --git a/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs b/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs index 8cb5e425f..0b681a58f 100644 --- a/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs +++ b/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs @@ -35,12 +35,8 @@ public override Score CreateReplayScore(IBeatmap beatmap) public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var d in drawables.OfType()) - { - d.Auto = true; - foreach (DrawableSentakkiHitObject nested in d.NestedHitObjects.Where(obj => obj is DrawableSentakkiHitObject)) - nested.Auto = true; - } + foreach (var d in drawables.OfType()) + d.AutoTouch = true; } } } diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs index 73cc35af1..f5295deb6 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs @@ -1,30 +1,118 @@ using osu.Game.Beatmaps; using osu.Game.Replays; using osu.Game.Rulesets.Sentakki.Objects; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays; using osuTK; using System; using System.Collections.Generic; +using System.Linq; namespace osu.Game.Rulesets.Sentakki.Replays { public class SentakkiAutoGenerator : AutoGenerator { + public const double RELEASE_DELAY = 20; + protected Replay Replay; protected List Frames => Replay.Frames; public new Beatmap Beatmap => (Beatmap)base.Beatmap; + private List lanedHitObjects; + public SentakkiAutoGenerator(IBeatmap beatmap) : base(beatmap) { Replay = new Replay(); - Frames.Add(new SentakkiReplayFrame { Position = new Vector2(-1000), Time = -500 }); } public override Replay Generate() { + //add some frames at the beginning so the cursor doesnt suddenly appear on the first note + Frames.Add(new SentakkiReplayFrame { Position = new Vector2(-1000), Time = -500 }); + + lanedHitObjects = Beatmap.HitObjects.Where(h => h is SentakkiLanedHitObject).ToList(); + + var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); + + var actions = new List(); + + foreach (var group in pointGroups) + { + foreach (var point in group) + { + switch (point) + { + case HitPoint _: + actions.Add(SentakkiAction.Key1 + point.Lane); + break; + + case ReleasePoint _: + actions.Remove(SentakkiAction.Key1 + point.Lane); + break; + } + } + + // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame. + if (Replay.Frames.Count == 0) + Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time - 1, new Vector2(-1000))); + + Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time, new Vector2(-1000), actions.ToArray())); + } + return Replay; } + + private IEnumerable generateActionPoints() + { + for (int i = 0; i < lanedHitObjects.Count; i++) + { + var currentObject = lanedHitObjects[i] as SentakkiLanedHitObject; + var nextObjectInColumn = GetNextObject(i) as SentakkiLanedHitObject; // Get the next object that requires pressing the same button + + double endTime = currentObject.GetEndTime(); + + bool canDelayKeyUp = nextObjectInColumn == null || + nextObjectInColumn.StartTime > endTime + RELEASE_DELAY; + + double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; + + yield return new HitPoint { Time = currentObject.StartTime, Lane = currentObject.Lane }; + + yield return new ReleasePoint { Time = endTime + calculatedDelay, Lane = currentObject.Lane }; + } + } + + protected override HitObject GetNextObject(int currentIndex) + { + int desiredLane = (lanedHitObjects[currentIndex] as SentakkiLanedHitObject).Lane; + + for (int i = currentIndex + 1; i < lanedHitObjects.Count; i++) + { + if ((lanedHitObjects[i] as SentakkiLanedHitObject).Lane == desiredLane) + return lanedHitObjects[i]; + } + + return null; + } + + private interface IActionPoint + { + double Time { get; set; } + int Lane { get; set; } + } + + private struct HitPoint : IActionPoint + { + public double Time { get; set; } + public int Lane { get; set; } + } + + private struct ReleasePoint : IActionPoint + { + public double Time { get; set; } + public int Lane { get; set; } + } } } From 228091c7f280a351e2bbbdab2548208b6dc598c5 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Oct 2020 13:04:11 +0200 Subject: [PATCH 02/13] Remove now unused Auto bindable in DHOs Now the tests are kinda fucky, since nested objects don't really know about the "fake auto" --- .../Objects/TestSceneBreakNote.cs | 27 +++++++++++++++++-- .../Objects/TestSceneHoldNote.cs | 27 +++++++++++++++++-- .../Objects/TestSceneSlideNote.cs | 9 ++----- .../Objects/TestSceneTapNote.cs | 27 +++++++++++++++++-- .../Objects/TestSceneTouchHold.cs | 2 +- .../Objects/TestSceneTouchNote.cs | 2 +- .../Objects/Drawables/DrawableHold.cs | 4 +-- .../Objects/Drawables/DrawableHoldHead.cs | 3 --- .../Drawables/DrawableSentakkiHitObject.cs | 7 ----- .../Objects/Drawables/DrawableSlide.cs | 2 -- .../Objects/Drawables/DrawableSlideBody.cs | 1 - .../Objects/Drawables/DrawableSlideNode.cs | 2 +- .../Objects/Drawables/DrawableTap.cs | 3 --- .../Objects/Drawables/DrawableTouch.cs | 2 +- .../Objects/Drawables/DrawableTouchHold.cs | 2 +- 15 files changed, 83 insertions(+), 37 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs index 4ab1f740b..606602812 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs @@ -39,13 +39,36 @@ private void testSingle(bool auto = false) circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - Add(new DrawableTap(circle) + Add(new TestDrawableTap(circle, auto) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = depthIndex++, - Auto = auto }); } + + protected class TestDrawableTap : DrawableTap + { + private readonly bool auto; + + public TestDrawableTap(Tap h, bool auto) + : base(h) + { + this.auto = auto; + } + + public void TriggerJudgement() => UpdateResult(true); + + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && timeOffset > 0) + { + // force success + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + else + base.CheckForResult(userTriggered, timeOffset); + } + } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs index 5454241c7..648cf9c5c 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs @@ -49,13 +49,36 @@ private void testSingle(double duration, bool auto = false) circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - Add(new DrawableHold(circle) + Add(new TestDrawableHold(circle, auto) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = depthIndex++, - Auto = auto }); } + + protected class TestDrawableHold : DrawableHold + { + private readonly bool auto; + + public TestDrawableHold(Hold h, bool auto) + : base(h) + { + this.auto = auto; + } + + public void TriggerJudgement() => UpdateResult(true); + + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && timeOffset > 0) + { + // force success + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + else + base.CheckForResult(userTriggered, timeOffset); + } + } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs index 6ce9d518d..725cab73a 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs @@ -55,18 +55,13 @@ private void testSingle(double duration, bool auto = false) slide.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - DrawableSlide dSlide; - - Add(dSlide = new DrawableSlide(slide) + Add(new DrawableSlide(slide) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = depthIndex++, - Auto = auto + AutoTouch = auto }); - - foreach (DrawableSentakkiHitObject nested in dSlide.NestedHitObjects) - nested.Auto = auto; } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs index 442219038..b4dfb1d85 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs @@ -38,13 +38,36 @@ private void testSingle(bool auto = false) circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - Add(new DrawableTap(circle) + Add(new TestDrawableTap(circle, auto) { Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = depthIndex++, - Auto = auto }); } + + protected class TestDrawableTap : DrawableTap + { + private readonly bool auto; + + public TestDrawableTap(Tap h, bool auto) + : base(h) + { + this.auto = auto; + } + + public void TriggerJudgement() => UpdateResult(true); + + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + if (auto && !userTriggered && timeOffset > 0) + { + // force success + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + else + base.CheckForResult(userTriggered, timeOffset); + } + } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs index 4b606e088..41852f381 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs @@ -43,7 +43,7 @@ private void testSingle(bool auto = false) Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = depthIndex++, - Auto = auto + AutoTouch = auto }); } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs index 855910556..233a6fda5 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs @@ -44,7 +44,7 @@ private void testSingle(bool auto = false) Anchor = Anchor.Centre, Origin = Anchor.Centre, Depth = depthIndex++, - Auto = auto + AutoTouch = auto }); } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs index 1ac089956..9af2f484b 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs @@ -84,7 +84,6 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, AccentColour = { BindTarget = AccentColour }, - AutoBindable = { BindTarget = AutoBindable } }; } @@ -122,7 +121,7 @@ protected override void UpdateInitialTransforms() .Delay(animTime - stretchTime) .ResizeHeightTo(75, stretchTime); - if (HoldStartTime == null && !Auto) + if (HoldStartTime == null) NoteBody.Note.Delay(animTime).FadeColour(Color4.Gray, 100); HitObjectLine.ScaleTo(1, animTime); @@ -155,7 +154,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) if (!headContainer.First().Result.HasResult) headContainer.First().MissForcefully(); - if (Auto) result = HitResult.Great; ApplyResult(r => r.Type = result); } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHoldHead.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHoldHead.cs index b645e2e53..5b6b8d20e 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHoldHead.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHoldHead.cs @@ -19,9 +19,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) if (!userTriggered) { - if (Auto && timeOffset > 0) - ApplyResult(r => r.Type = HitResult.Great); - if (!HitObject.HitWindows.CanBeHit(timeOffset)) ApplyResult(r => r.Type = HitResult.Miss); return; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs index 71a42b19a..20043d807 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs @@ -29,13 +29,6 @@ public class DrawableSentakkiHitObject : DrawableHitObject public bool IsHidden = false; public bool IsFadeIn = false; - public readonly BindableBool AutoBindable = new BindableBool(false); - public bool Auto - { - get => AutoBindable.Value; - set => AutoBindable.Value = value; - } - // Used in the editor public bool IsVisible => Time.Current >= HitObject.StartTime - AnimationDuration.Value; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlide.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlide.cs index f6b566f06..288cf67ab 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlide.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlide.cs @@ -65,7 +65,6 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) case Tap x: return new DrawableSlideTap(x, this) { - AutoBindable = { BindTarget = AutoBindable }, Anchor = Anchor.Centre, Origin = Anchor.Centre, AccentColour = { BindTarget = AccentColour } @@ -73,7 +72,6 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) case SlideBody slideBody: return new DrawableSlideBody(slideBody) { - AutoBindable = { BindTarget = AutoBindable }, AutoTouchBindable = { BindTarget = AutoTouchBindable }, Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs index 5571e7a36..2648e138f 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs @@ -155,7 +155,6 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) Anchor = Anchor.Centre, Origin = Anchor.Centre, AccentColour = { BindTarget = AccentColour }, - AutoBindable = { BindTarget = AutoBindable }, AutoTouchBindable = { BindTarget = AutoTouchBindable } }; } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs index 7d95a12c9..f1f32afed 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs @@ -93,7 +93,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) { if (!userTriggered || AutoTouch) { - if (timeOffset > 0 && (Auto || AutoTouch)) + if (timeOffset > 0 && AutoTouch) ApplyResult(r => r.Type = r.Judgement.MaxResult); return; } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs index 5add8c9d8..c3f02a889 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs @@ -68,9 +68,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) if (!userTriggered) { - if (Auto && timeOffset > 0) - ApplyResult(r => r.Type = HitResult.Great); - if (!HitObject.HitWindows.CanBeHit(timeOffset)) { ApplyResult(r => r.Type = HitResult.Miss); diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs index 36e819744..c730f4a7f 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs @@ -142,7 +142,7 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) if (!userTriggered || AutoTouch) { - if ((Auto || AutoTouch) && timeOffset > 0) + if (AutoTouch && timeOffset > 0) ApplyResult(r => r.Type = HitResult.Great); if (!HitObject.HitWindows.CanBeHit(timeOffset)) diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs index e08b89997..a2ef7e1ef 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs @@ -126,7 +126,7 @@ protected override void Update() bool isTouched = touchInput.ActiveSources.Any(s => ReceivePositionalInputAt(touchInput.GetTouchPosition(s) ?? new Vector2(float.MinValue))); isHitting.Value = Time.Current >= HitObject.StartTime && Time.Current <= (HitObject as IHasDuration)?.EndTime - && (Auto || AutoTouch || isTouched || ((SentakkiActionInputManager?.PressedActions.Any() ?? false) && IsHovered)); + && (AutoTouch || isTouched || ((SentakkiActionInputManager?.PressedActions.Any() ?? false) && IsHovered)); } protected override void UpdateStateTransforms(ArmedState state) From e4751b6bf82ef41025ad39e72af60c936749aadd Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Oct 2020 15:35:16 +0200 Subject: [PATCH 03/13] Rewrite HitObject tests --- .../Objects/TestSceneBreakNote.cs | 86 ++++++++----------- .../Objects/TestSceneHitObject.cs | 42 +++++++++ .../Objects/TestSceneHoldNote.cs | 73 +++------------- .../Objects/TestSceneSlideNote.cs | 45 +++------- .../Objects/TestSceneTapNote.cs | 66 ++++---------- .../Objects/TestSceneTouchHold.cs | 40 +++------ .../Objects/TestSceneTouchNote.cs | 44 ++++------ .../Beatmaps/SentakkiBeatmapConverter.cs | 2 +- .../Objects/Drawables/DrawableSlideBody.cs | 2 + .../Objects/Drawables/DrawableSlideNode.cs | 4 +- 10 files changed, 152 insertions(+), 252 deletions(-) create mode 100644 osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs index 606602812..219818f1d 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs @@ -9,66 +9,52 @@ using osuTK; using osuTK.Graphics; using System.Linq; +using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - [TestFixture] - public class TestSceneBreakNote : OsuTestScene + public class TestSceneBreakNote : TestSceneHitObject { - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - - public TestSceneBreakNote() - { - base.Content.Add(content = new SentakkiInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Single", () => testSingle()); - AddStep("Hit Single", () => testSingle(true)); - AddUntilStep("Wait for object despawn", () => !Children.Any(h => (h is DrawableSentakkiHitObject) && (h as DrawableSentakkiHitObject).AllJudged == false)); - } - - private void testSingle(bool auto = false) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { - var circle = new Tap + var beatmap = new Beatmap() { - IsBreak = true, - StartTime = Time.Current + 1000, + BeatmapInfo = + { + Ruleset = CreateRuleset()?.RulesetInfo ?? ruleset + }, }; - - circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - - Add(new TestDrawableTap(circle, auto) + for (int i = 0; i < 8; ++i) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Depth = depthIndex++, - }); - } - - protected class TestDrawableTap : DrawableTap - { - private readonly bool auto; - - public TestDrawableTap(Tap h, bool auto) - : base(h) - { - this.auto = auto; - } - - public void TriggerJudgement() => UpdateResult(true); - - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - if (auto && !userTriggered && timeOffset > 0) + beatmap.HitObjects.Add(new Tap + { + IsBreak = true, + StartTime = 500, + Lane = i + }); + beatmap.HitObjects.Add(new Hold { - // force success - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - else - base.CheckForResult(userTriggered, timeOffset); + IsBreak = true, + StartTime = 1000, + Duration = 300, + Lane = (i + 3).NormalizePath() + }); + beatmap.HitObjects.Add(new Slide + { + IsBreak = true, + SlideInfoList = new List + { + new SentakkiSlideInfo { + ID = 1, + Duration = 500, + } + }, + StartTime = 1500, + Lane = (i + 7).NormalizePath() + }); } + + return beatmap; } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs new file mode 100644 index 000000000..802872504 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs @@ -0,0 +1,42 @@ +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Rulesets.Sentakki.Objects; +using osu.Game.Rulesets.Sentakki.Objects.Drawables; +using osu.Game.Tests.Visual; +using osuTK; +using osuTK.Graphics; +using System.Linq; +using System.Collections.Generic; +using osu.Game.Rulesets.Replays; + +namespace osu.Game.Rulesets.Sentakki.Tests.Objects +{ + public abstract class TestSceneHitObject : PlayerTestScene + { + protected override Ruleset CreatePlayerRuleset() => new SentakkiRuleset(); + + protected override bool HasCustomSteps => true; + + private bool auto = false; + protected override bool Autoplay => auto; + + [Test] + public void TestMisses() + { + AddStep("Turn off auto", () => auto = false); + CreateTest(null); + AddUntilStep("Wait until all hitobjects are judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.AllJudged)); + } + + [Test] + public void TestHits() + { + AddStep("Turn on auto", () => auto = true); + CreateTest(null); + AddUntilStep("Wait until all hitobjects are judged", () => Player.DrawableRuleset.Playfield.AllHitObjects.All(h => h.AllJudged)); + } + } +} diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs index 648cf9c5c..800123243 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs @@ -12,73 +12,28 @@ namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - [TestFixture] - public class TestSceneHoldNote : OsuTestScene + public class TestSceneHoldNote : TestSceneHitObject { - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - - public TestSceneHoldNote() + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { - base.Content.Add(content = new SentakkiInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Insane Short", () => testSingle(100)); - AddStep("Hit Insane Short", () => testSingle(100, true)); - AddStep("Miss Very Short", () => testSingle(200)); - AddStep("Hit Very Short", () => testSingle(200, true)); - AddStep("Miss Short", () => testSingle(500)); - AddStep("Hit Short", () => testSingle(500, true)); - AddStep("Miss Medium", () => testSingle(750)); - AddStep("Hit Medium", () => testSingle(750, true)); - AddStep("Miss Long", () => testSingle(1000)); - AddStep("Hit Long", () => testSingle(1000, true)); - AddStep("Miss Very Long", () => testSingle(3000)); - AddStep("Hit Very Long", () => testSingle(3000, true)); - AddUntilStep("Wait for object despawn", () => !Children.Any(h => (h is DrawableSentakkiHitObject) && (h as DrawableSentakkiHitObject).AllJudged == false)); - } - - private void testSingle(double duration, bool auto = false) - { - var circle = new Hold + var beatmap = new Beatmap() { - StartTime = Time.Current + 1000, - EndTime = Time.Current + 1000 + duration, + BeatmapInfo = + { + Ruleset = CreateRuleset()?.RulesetInfo ?? ruleset + }, }; - circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - - Add(new TestDrawableHold(circle, auto) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Depth = depthIndex++, - }); - } - - protected class TestDrawableHold : DrawableHold - { - private readonly bool auto; - - public TestDrawableHold(Hold h, bool auto) - : base(h) - { - this.auto = auto; - } - - public void TriggerJudgement() => UpdateResult(true); - - protected override void CheckForResult(bool userTriggered, double timeOffset) + for (int i = 0; i < 8; ++i) { - if (auto && !userTriggered && timeOffset > 0) + beatmap.HitObjects.Add(new Hold { - // force success - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - else - base.CheckForResult(userTriggered, timeOffset); + StartTime = 500 + (200 * i), + Duration = 100 + (200 * i), + Lane = i + }); } + return beatmap; } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs index 725cab73a..849ea0ea4 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs @@ -11,30 +11,19 @@ namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - [TestFixture] - public class TestSceneSlideNote : OsuTestScene + public class TestSceneSlideNote : TestSceneHitObject { - private readonly Container content; - protected override Container Content => content; - - protected override Ruleset CreateRuleset() => new SentakkiRuleset(); - - private int depthIndex; - - public TestSceneSlideNote() - { - base.Content.Add(content = new SentakkiInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Single", () => testSingle(2000)); - AddStep("Hit Single", () => testSingle(2000, true)); - AddUntilStep("Wait for object despawn", () => !Children.Any(h => (h is DrawableSentakkiHitObject) && (h as DrawableSentakkiHitObject).AllJudged == false)); - } - - private void testSingle(double duration, bool auto = false) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { - var slide = new Slide + var beatmap = new Beatmap() + { + BeatmapInfo = + { + Ruleset = CreateRuleset()?.RulesetInfo ?? ruleset + }, + }; + beatmap.HitObjects.Add(new Slide { - IsBreak = true, SlideInfoList = new List { new SentakkiSlideInfo { @@ -50,18 +39,10 @@ private void testSingle(double duration, bool auto = false) Duration = 2000, } }, - StartTime = Time.Current + 1000, - }; - - slide.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - - Add(new DrawableSlide(slide) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Depth = depthIndex++, - AutoTouch = auto + StartTime = 500, }); + return beatmap; } + } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs index b4dfb1d85..cb61d63de 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs @@ -9,65 +9,29 @@ using osuTK; using osuTK.Graphics; using System.Linq; +using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - [TestFixture] - public class TestSceneTapNote : OsuTestScene + public class TestSceneTapNote : TestSceneHitObject { - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - - public TestSceneTapNote() - { - base.Content.Add(content = new SentakkiInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Single", () => testSingle()); - AddStep("Hit Single", () => testSingle(true)); - AddUntilStep("Wait for object despawn", () => !Children.Any(h => (h is DrawableSentakkiHitObject) && (h as DrawableSentakkiHitObject).AllJudged == false)); - } - - private void testSingle(bool auto = false) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { - var circle = new Tap + var beatmap = new Beatmap() { - StartTime = Time.Current + 1000, + BeatmapInfo = + { + Ruleset = CreateRuleset()?.RulesetInfo ?? ruleset + }, }; - - circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - - Add(new TestDrawableTap(circle, auto) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Depth = depthIndex++, - }); - } - - protected class TestDrawableTap : DrawableTap - { - private readonly bool auto; - - public TestDrawableTap(Tap h, bool auto) - : base(h) - { - this.auto = auto; - } - - public void TriggerJudgement() => UpdateResult(true); - - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - if (auto && !userTriggered && timeOffset > 0) + for (int i = 0; i < 8; ++i) + beatmap.HitObjects.Add(new Tap { - // force success - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - else - base.CheckForResult(userTriggered, timeOffset); - } + StartTime = 500 + (100 * i), + Lane = i + }); + + return beatmap; } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs index 41852f381..c4a9478d7 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs @@ -11,40 +11,24 @@ namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - [TestFixture] - public class TestSceneTouchHold : OsuTestScene + public class TestSceneTouchHold : TestSceneHitObject { - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - - public TestSceneTouchHold() + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { - base.Content.Add(content = new SentakkiInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Single", () => testSingle()); - AddStep("Hit Single", () => testSingle(true)); - AddUntilStep("Wait for object despawn", () => !Children.Any(h => (h is DrawableSentakkiHitObject) && (h as DrawableSentakkiHitObject).AllJudged == false)); - } - - private void testSingle(bool auto = false) - { - var circle = new TouchHold + var beatmap = new Beatmap() { - StartTime = Time.Current + 1000, - Duration = 5000, + BeatmapInfo = + { + Ruleset = CreateRuleset()?.RulesetInfo ?? ruleset + }, }; - - circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - - Add(new DrawableTouchHold(circle) + beatmap.HitObjects.Add(new TouchHold { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Depth = depthIndex++, - AutoTouch = auto + StartTime = 500, + Duration = 1500 }); + + return beatmap; } } } diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs index 233a6fda5..bca5bbce6 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs @@ -9,43 +9,29 @@ using osuTK; using osuTK.Graphics; using System.Linq; +using osu.Framework.Utils; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - [TestFixture] - public class TestSceneTouchNote : OsuTestScene + public class TestSceneTouchNote : TestSceneHitObject { - private readonly Container content; - protected override Container Content => content; - - private int depthIndex; - - public TestSceneTouchNote() - { - base.Content.Add(content = new SentakkiInputManager(new RulesetInfo { ID = 0 })); - - AddStep("Miss Single", () => testSingle()); - AddStep("Hit Single", () => testSingle(true)); - AddUntilStep("Wait for object despawn", () => !Children.Any(h => (h is DrawableSentakkiHitObject) && (h as DrawableSentakkiHitObject).AllJudged == false)); - } - - private void testSingle(bool auto = false) + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { - var circle = new Touch + var beatmap = new Beatmap() { - StartTime = Time.Current + 1000, - Position = Vector2.Zero, + BeatmapInfo = + { + Ruleset = CreateRuleset()?.RulesetInfo ?? ruleset + }, }; + for (int i = 0; i < 8; ++i) + beatmap.HitObjects.Add(new Touch + { + StartTime = 500 + (100 * i), + Position = SentakkiExtensions.GetCircularPosition(RNG.NextSingle(250), RNG.NextSingle(360)) + }); - circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); - - Add(new DrawableTouch(circle) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Depth = depthIndex++, - AutoTouch = auto - }); + return beatmap; } } } diff --git a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs index ddec6e968..faa2c5b4a 100644 --- a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs +++ b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs @@ -23,7 +23,7 @@ public class SentakkiBeatmapConverter : BeatmapConverter { // todo: Check for conversion types that should be supported (ie. Beatmap.HitObjects.Any(h => h is IHasXPosition)) // https://github.com/ppy/osu/tree/master/osu.Game/Rulesets/Objects/Types - public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasPosition); + public override bool CanConvert() => true; public Bindable EnabledExperiments = new Bindable(); diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs index 2648e138f..b7acc3ec7 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs @@ -187,6 +187,8 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) ApplyResult(r => r.Type = HitResult.Meh); else ApplyResult(r => r.Type = HitResult.Miss); + + SlideNodes.Last().ForceJudgement(false); } return; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs index f1f32afed..783b5e422 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs @@ -52,7 +52,7 @@ protected override void LoadComplete() OnNewResult += (DrawableHitObject hitObject, JudgementResult result) => { - hitPreviousNodes(result.Type >= HitResult.Great); + hitPreviousNodes(result.Type == result.Judgement.MaxResult); if (result.IsHit) Slide.Slidepath.Progress = (HitObject as SlideBody.SlideNode).Progress; }; @@ -123,7 +123,7 @@ protected override void Update() public override void PlaySamples() { base.PlaySamples(); - if (ThisIndex == 0 && playSlideSample.Value && slideSound != null && Result.Type != HitResult.Miss && (!gameplayClock?.IsSeeking ?? false)) + if (ThisIndex == 0 && playSlideSample.Value && slideSound != null && Result.Type != Result.Judgement.MinResult && (!gameplayClock?.IsSeeking ?? false)) { const float balance_adjust_amount = 0.4f; slideSound.Balance.Value = balance_adjust_amount * (userPositionalHitSounds.Value ? SamplePlaybackPosition - 0.5f : 0); From 9fa8e079cc8e013946ee3f1d68386b64354c4672 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Oct 2020 15:47:14 +0200 Subject: [PATCH 04/13] Remove excess usings in HitObject tests --- .../Objects/TestSceneBreakNote.cs | 13 ++----------- .../Objects/TestSceneHitObject.cs | 12 +----------- .../Objects/TestSceneHoldNote.cs | 13 ++----------- .../Objects/TestSceneSlideNote.cs | 9 +-------- .../Objects/TestSceneTapNote.cs | 14 ++------------ .../Objects/TestSceneTouchHold.cs | 12 ++---------- .../Objects/TestSceneTouchNote.cs | 8 +------- 7 files changed, 11 insertions(+), 70 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs index 219818f1d..502585898 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs @@ -1,19 +1,10 @@ -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; -using osu.Game.Tests.Visual; -using osuTK; -using osuTK.Graphics; -using System.Linq; using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public class TestSceneBreakNote : TestSceneHitObject + public class TestSceneBreakNote : TestSceneSentakkiHitObject { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs index 802872504..fa99f9888 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHitObject.cs @@ -1,20 +1,10 @@ using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; using osu.Game.Tests.Visual; -using osuTK; -using osuTK.Graphics; using System.Linq; -using System.Collections.Generic; -using osu.Game.Rulesets.Replays; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public abstract class TestSceneHitObject : PlayerTestScene + public abstract class TestSceneSentakkiHitObject : PlayerTestScene { protected override Ruleset CreatePlayerRuleset() => new SentakkiRuleset(); diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs index 800123243..6d585472a 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs @@ -1,18 +1,9 @@ -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; -using osu.Game.Tests.Visual; -using osuTK; -using osuTK.Graphics; -using System.Linq; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public class TestSceneHoldNote : TestSceneHitObject + public class TestSceneHoldNote : TestSceneSentakkiHitObject { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs index 849ea0ea4..d83c6d6b8 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs @@ -1,17 +1,10 @@ -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; -using osu.Game.Tests.Visual; -using System.Linq; using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public class TestSceneSlideNote : TestSceneHitObject + public class TestSceneSlideNote : TestSceneSentakkiHitObject { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs index cb61d63de..e8629a67d 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs @@ -1,19 +1,9 @@ -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; -using osu.Game.Tests.Visual; -using osuTK; -using osuTK.Graphics; -using System.Linq; -using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public class TestSceneTapNote : TestSceneHitObject + public class TestSceneTapNote : TestSceneSentakkiHitObject { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs index c4a9478d7..4881a87dc 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs @@ -1,17 +1,9 @@ -using NUnit.Framework; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; -using osu.Game.Tests.Visual; -using osuTK; -using System.Linq; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public class TestSceneTouchHold : TestSceneHitObject + public class TestSceneTouchHold : TestSceneSentakkiHitObject { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs index bca5bbce6..8e9a23568 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchNote.cs @@ -2,18 +2,12 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Sentakki.Objects; -using osu.Game.Rulesets.Sentakki.Objects.Drawables; -using osu.Game.Tests.Visual; -using osuTK; -using osuTK.Graphics; -using System.Linq; using osu.Framework.Utils; namespace osu.Game.Rulesets.Sentakki.Tests.Objects { - public class TestSceneTouchNote : TestSceneHitObject + public class TestSceneTouchNote : TestSceneSentakkiHitObject { protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) { From 97c6e6c42a7270436220686439a59f63fb5ebde8 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Sun, 4 Oct 2020 23:50:10 +0200 Subject: [PATCH 05/13] Also generate actual touch input in autoplay No more faking touch for Sen and Mai either. ;) Some inaccuracies here and there, and the AutoTouch bindable haven't been removed yet, but at least we know this works --- .../Mods/SentakkiModAutoplay.cs | 4 +- .../Objects/Drawables/DrawableSlideBody.cs | 2 - .../Objects/Drawables/DrawableTouchHold.cs | 2 + .../Replays/SentakkiAutoGenerator.cs | 130 ++++++++++++++---- .../SentakkiFramedReplayInputHandler.cs | 27 ++++ .../Replays/SentakkiReplayFrame.cs | 43 ++++-- .../SentakkiExtensions.cs | 18 +++ .../UI/Components/TouchVisualization.cs | 54 ++++++++ osu.Game.Rulesets.Sentakki/UI/Lane.cs | 1 + .../UI/LanedPlayfield.cs | 1 + .../UI/SentakkiPlayfield.cs | 8 +- .../UI/SentakkiReplayRecorder.cs | 3 +- 12 files changed, 248 insertions(+), 45 deletions(-) create mode 100644 osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs diff --git a/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs b/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs index 0b681a58f..ee6c0bb3c 100644 --- a/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs +++ b/osu.Game.Rulesets.Sentakki/Mods/SentakkiModAutoplay.cs @@ -35,8 +35,8 @@ public override Score CreateReplayScore(IBeatmap beatmap) public void ApplyToDrawableHitObjects(IEnumerable drawables) { - foreach (var d in drawables.OfType()) - d.AutoTouch = true; + //foreach (var d in drawables.OfType()) + // d.AutoTouch = true; } } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs index b7acc3ec7..2648e138f 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideBody.cs @@ -187,8 +187,6 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) ApplyResult(r => r.Type = HitResult.Meh); else ApplyResult(r => r.Type = HitResult.Miss); - - SlideNodes.Last().ForceJudgement(false); } return; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs index a2ef7e1ef..efee87a2e 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs @@ -127,6 +127,8 @@ protected override void Update() isHitting.Value = Time.Current >= HitObject.StartTime && Time.Current <= (HitObject as IHasDuration)?.EndTime && (AutoTouch || isTouched || ((SentakkiActionInputManager?.PressedActions.Any() ?? false) && IsHovered)); + + Console.WriteLine("SS Centre: " + ScreenSpaceDrawQuad.Centre); } protected override void UpdateStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs index f5295deb6..ebe9991d7 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs @@ -7,6 +7,9 @@ using System; using System.Collections.Generic; using System.Linq; +using osu.Game.Rulesets.Sentakki.Scoring; +using Microsoft.EntityFrameworkCore.Query.Expressions; +using System.Diagnostics; namespace osu.Game.Rulesets.Sentakki.Replays { @@ -19,8 +22,6 @@ public class SentakkiAutoGenerator : AutoGenerator public new Beatmap Beatmap => (Beatmap)base.Beatmap; - private List lanedHitObjects; - public SentakkiAutoGenerator(IBeatmap beatmap) : base(beatmap) { @@ -32,11 +33,10 @@ public override Replay Generate() //add some frames at the beginning so the cursor doesnt suddenly appear on the first note Frames.Add(new SentakkiReplayFrame { Position = new Vector2(-1000), Time = -500 }); - lanedHitObjects = Beatmap.HitObjects.Where(h => h is SentakkiLanedHitObject).ToList(); - var pointGroups = generateActionPoints().GroupBy(a => a.Time).OrderBy(g => g.First().Time); var actions = new List(); + var TouchReplayEvents = new TouchReplayEvent[10]; foreach (var group in pointGroups) { @@ -44,21 +44,28 @@ public override Replay Generate() { switch (point) { - case HitPoint _: - actions.Add(SentakkiAction.Key1 + point.Lane); + case KeyDown e: + actions.Add(SentakkiAction.Key1 + e.Lane); break; - case ReleasePoint _: - actions.Remove(SentakkiAction.Key1 + point.Lane); + case KeyUp e: + actions.Remove(SentakkiAction.Key1 + e.Lane); + break; + case TouchDown e: + TouchReplayEvents[e.PointNumber] = e.TouchReplayEvent; + break; + case TouchUp e: + TouchReplayEvents[e.PointNumber] = null; break; } } // todo: can be removed once FramedReplayInputHandler correctly handles rewinding before first frame. if (Replay.Frames.Count == 0) - Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time - 1, new Vector2(-1000))); + Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time - 1, new Vector2(-1000), Array.Empty())); + - Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time, new Vector2(-1000), actions.ToArray())); + Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time, new Vector2(-1000), TouchReplayEvents.ToList().ToArray(), actions.ToArray())); } return Replay; @@ -66,53 +73,122 @@ public override Replay Generate() private IEnumerable generateActionPoints() { - for (int i = 0; i < lanedHitObjects.Count; i++) - { - var currentObject = lanedHitObjects[i] as SentakkiLanedHitObject; - var nextObjectInColumn = GetNextObject(i) as SentakkiLanedHitObject; // Get the next object that requires pressing the same button + var touchPointInUsedUntil = new double?[10]; + for (int i = 0; i < Beatmap.HitObjects.Count; i++) + { + var currentObject = Beatmap.HitObjects[i]; double endTime = currentObject.GetEndTime(); - bool canDelayKeyUp = nextObjectInColumn == null || - nextObjectInColumn.StartTime > endTime + RELEASE_DELAY; - - double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; - - yield return new HitPoint { Time = currentObject.StartTime, Lane = currentObject.Lane }; + int nextAvailableTouchPoint() => Array.IndexOf(touchPointInUsedUntil, touchPointInUsedUntil.First(t => !t.HasValue || t.Value < currentObject.StartTime)); - yield return new ReleasePoint { Time = endTime + calculatedDelay, Lane = currentObject.Lane }; + switch (currentObject) + { + case SentakkiLanedHitObject laned: + var nextObjectInColumn = GetNextObject(i) as SentakkiLanedHitObject; // Get the next object that requires pressing the same button + + bool canDelayKeyUp = nextObjectInColumn == null || + nextObjectInColumn.StartTime > endTime + RELEASE_DELAY; + + double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; + + yield return new KeyDown { Time = currentObject.StartTime, Lane = laned.Lane }; + yield return new KeyUp { Time = endTime + calculatedDelay, Lane = laned.Lane }; + + if (laned is Slide s) + { + foreach (var slideInfo in s.SlideInfoList) + { + double delay = Beatmap.ControlPointInfo.TimingPointAt(currentObject.StartTime).BeatLength * slideInfo.ShootDelay / 2; + if (delay >= slideInfo.Duration - 50) delay = 0; + yield return new TouchDown + { + Time = currentObject.StartTime + delay, + TouchReplayEvent = new TouchReplayEvent(slideInfo.SlidePath.Path, slideInfo.Duration - delay, currentObject.StartTime + delay, s.Lane.GetRotationForLane() - 22.5f), + PointNumber = nextAvailableTouchPoint() + }; + yield return new TouchUp { Time = currentObject.StartTime + slideInfo.Duration, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = currentObject.StartTime + slideInfo.Duration; + } + } + break; + + case Touch t: + yield return new TouchDown + { + Time = currentObject.StartTime, + PointNumber = nextAvailableTouchPoint(), + TouchReplayEvent = new TouchReplayEvent(t.Position, 10, currentObject.StartTime) + }; + yield return new TouchUp { Time = endTime + 10, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + 10; + break; + + case TouchHold _: + yield return new TouchDown + { + Time = currentObject.StartTime, + PointNumber = nextAvailableTouchPoint(), + TouchReplayEvent = new TouchReplayEvent(Vector2.Zero, 10, currentObject.StartTime) + }; + yield return new TouchUp { Time = endTime + 10, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + 10; + break; + } } } protected override HitObject GetNextObject(int currentIndex) { - int desiredLane = (lanedHitObjects[currentIndex] as SentakkiLanedHitObject).Lane; + int desiredLane = (Beatmap.HitObjects[currentIndex] as SentakkiLanedHitObject).Lane; - for (int i = currentIndex + 1; i < lanedHitObjects.Count; i++) + for (int i = currentIndex + 1; i < Beatmap.HitObjects.Count; i++) { - if ((lanedHitObjects[i] as SentakkiLanedHitObject).Lane == desiredLane) - return lanedHitObjects[i]; + if (Beatmap.HitObjects[i] is SentakkiLanedHitObject laned && laned.Lane == desiredLane) + return Beatmap.HitObjects[i]; } return null; } + private interface IActionPoint { double Time { get; set; } + } + private interface ILanedActionPoint : IActionPoint + { int Lane { get; set; } } + private interface ITouchActionPoint : IActionPoint + { + TouchReplayEvent TouchReplayEvent { get; set; } + int PointNumber { get; set; } + } - private struct HitPoint : IActionPoint + private struct KeyDown : ILanedActionPoint { public double Time { get; set; } public int Lane { get; set; } } - private struct ReleasePoint : IActionPoint + private struct KeyUp : ILanedActionPoint { public double Time { get; set; } public int Lane { get; set; } } + private struct TouchDown : ITouchActionPoint + { + public double Time { get; set; } + public TouchReplayEvent TouchReplayEvent { get; set; } + public int PointNumber { get; set; } + + } + private struct TouchUp : ITouchActionPoint + { + public double Time { get; set; } + public TouchReplayEvent TouchReplayEvent { get; set; } + public int PointNumber { get; set; } + } } } diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiFramedReplayInputHandler.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiFramedReplayInputHandler.cs index 515c20f16..8c861ab73 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiFramedReplayInputHandler.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiFramedReplayInputHandler.cs @@ -1,10 +1,13 @@ using osu.Framework.Input.StateChanges; +using osu.Framework.Input; using osu.Framework.Utils; using osu.Game.Replays; using osu.Game.Rulesets.Replays; using osuTK; using System.Collections.Generic; using System.Diagnostics; +using System.Linq; +using System; namespace osu.Game.Rulesets.Sentakki.Replays { @@ -34,10 +37,34 @@ protected Vector2 Position public override void CollectPendingInputs(List inputs) { + if (CurrentFrame != null && CurrentFrame.TouchReplayEvents != null) + { + for (int i = 0; i < 10; ++i) + { + var activeTouchPoint = CurrentFrame.TouchReplayEvents[i]; + if (activeTouchPoint == null) + { + inputs.Add(new TouchInput(new Touch((TouchSource)i, Vector2.Zero), false)); + } + else + { + //get point position + double percentage = Math.Clamp(Interpolation.ValueAt(CurrentTime.Value, 0D, 1D, activeTouchPoint.StartTime, activeTouchPoint.StartTime + activeTouchPoint.Duration), 0, 1); + + var position = activeTouchPoint.MovementPath.PositionAt(percentage); + + position = SentakkiExtensions.RotatePointAroundOrigin(position, Vector2.Zero, activeTouchPoint.Rotation) + new Vector2(300); + + inputs.Add(new TouchInput(new Touch((TouchSource)i, GamefieldToScreenSpace(position)), true)); + } + } + } + inputs.Add(new MousePositionAbsoluteInput { Position = GamefieldToScreenSpace(Position), }); + inputs.Add(new ReplayState { PressedActions = CurrentFrame?.Actions ?? new List(), diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs index 7d32f4b23..998edc891 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs @@ -2,25 +2,30 @@ using osu.Game.Beatmaps; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays.Types; +using osu.Framework.Input; using osuTK; +using osu.Framework.Input.Events; namespace osu.Game.Rulesets.Sentakki.Replays { public class SentakkiReplayFrame : ReplayFrame, IConvertibleReplayFrame { - public ReplayEvent NoteEvent = ReplayEvent.none; public Vector2 Position; public List Actions = new List(); + public TouchReplayEvent[] TouchReplayEvents { get; set; } + public SentakkiReplayFrame() { } - public SentakkiReplayFrame(double time, Vector2 position, params SentakkiAction[] actions) + public SentakkiReplayFrame(double time, Vector2 position, TouchReplayEvent[] TRE, params SentakkiAction[] actions) : base(time) { Position = position; + TouchReplayEvents = TRE; Actions.AddRange(actions); } @@ -42,15 +47,31 @@ public LegacyReplayFrame ToLegacy(IBeatmap beatmap) return new LegacyReplayFrame(Time, Position.X, Position.Y, state); } } - - public enum ReplayEvent + public class TouchReplayEvent { - none, - TapDown, - TapUp, - TouchHoldDown, - TouchHoldUp, - HoldDown, - HoldUp + public TouchReplayEvent(Vector2 Position, double Duration, double startTime, float rotation = 0) + { + MovementPath = new SliderPath(new PathControlPoint[]{ + new PathControlPoint(Position) + }); + this.Duration = Duration; + StartTime = startTime; + Rotation = rotation; + } + + public TouchReplayEvent(SliderPath path, double Duration, double startTime, float rotation = 0) + { + MovementPath = path; + this.Duration = Duration; + StartTime = startTime; + Rotation = rotation; + } + + + public SliderPath MovementPath; + public double Duration; + public double StartTime; + public float Rotation = 0; + } } diff --git a/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs b/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs index a9a252d40..be672b2a6 100644 --- a/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs +++ b/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs @@ -1,5 +1,6 @@ using osu.Game.Rulesets.Sentakki.UI; using osuTK; +using osu.Framework.Utils; using System; namespace osu.Game.Rulesets.Sentakki @@ -66,5 +67,22 @@ public static int GetNoteLaneFromDegrees(this float degrees) } return result; } + + public static Vector2 RotatePointAroundOrigin(Vector2 point, Vector2 origin, float angle) + { + angle = -angle; + + point.X -= origin.X; + point.Y -= origin.Y; + + Vector2 ret; + ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle)); + ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle)); + + ret.X += origin.X; + ret.Y += origin.Y; + + return ret; + } } } diff --git a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs new file mode 100644 index 000000000..8555952ee --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs @@ -0,0 +1,54 @@ +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Rulesets.Sentakki.Configuration; +using osuTK; +using osuTK.Graphics; +using System; +using osu.Game.Online.API; +using osu.Game.Users; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Sentakki.UI.Components +{ + public class TouchVisualization : CompositeDrawable + { + private SentakkiInputManager sentakkiActionInputManager; + internal SentakkiInputManager SentakkiActionInputManager => sentakkiActionInputManager ??= GetContainingInputManager() as SentakkiInputManager; + + private readonly Container dots; + + public TouchVisualization() + { + RelativeSizeAxes = Axes.Both; + AlwaysPresent = true; + InternalChildren = new Drawable[] + { + dots = new Container() + }; + } + + protected override void Update() + { + base.Update(); + dots.Clear(); + + var touchInput = SentakkiActionInputManager.CurrentState.Touch; + foreach (var points in touchInput.ActiveSources) + { + dots.Add(new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(20), + Position = ToLocalSpace(touchInput.GetTouchPosition(points) ?? new Vector2(float.MinValue)), + Colour = Color4.Green, + }); + } + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/UI/Lane.cs b/osu.Game.Rulesets.Sentakki/UI/Lane.cs index fd70affd2..2d4a5fe6b 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Lane.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Lane.cs @@ -60,6 +60,7 @@ protected override void Update() private void handleKeyPress(ValueChangedEvent keys) { + if (sentakkiActionInputManager.ReplayInputHandler != null) return; if (keys.NewValue > keys.OldValue || keys.NewValue == 0) SentakkiActionInputManager.TriggerReleased(SentakkiAction.Key1 + ((Lane)Parent).LaneNumber); diff --git a/osu.Game.Rulesets.Sentakki/UI/LanedPlayfield.cs b/osu.Game.Rulesets.Sentakki/UI/LanedPlayfield.cs index 08e6700aa..b037d06db 100644 --- a/osu.Game.Rulesets.Sentakki/UI/LanedPlayfield.cs +++ b/osu.Game.Rulesets.Sentakki/UI/LanedPlayfield.cs @@ -7,6 +7,7 @@ using System; using System.Linq; using System.Collections.Generic; +using osu.Framework.Allocation; namespace osu.Game.Rulesets.Sentakki.UI { diff --git a/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs b/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs index 63a0b1b27..4196a6790 100644 --- a/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs +++ b/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs @@ -57,11 +57,15 @@ public SentakkiPlayfield() new PlayfieldVisualisation(), ring = new SentakkiRing(), lanedPlayfield = new LanedPlayfield(), - HitObjectContainer, // This only contains Touch and TouchHolds, which should appear above others note types. Might consider separating to another playfield. + HitObjectContainer.With(h => { + h.Origin = Anchor.Centre; + h.Anchor = Anchor.Centre; + }), // This only contains Touch and TouchHolds, which should appear above others note types. Might consider separating to another playfield. judgementLayer = new JudgementContainer { RelativeSizeAxes = Axes.Both, - } + }, + new TouchVisualization() }); AddNested(lanedPlayfield); } diff --git a/osu.Game.Rulesets.Sentakki/UI/SentakkiReplayRecorder.cs b/osu.Game.Rulesets.Sentakki/UI/SentakkiReplayRecorder.cs index f3f6d5ad2..6134d135e 100644 --- a/osu.Game.Rulesets.Sentakki/UI/SentakkiReplayRecorder.cs +++ b/osu.Game.Rulesets.Sentakki/UI/SentakkiReplayRecorder.cs @@ -4,6 +4,7 @@ using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.UI; using osuTK; +using System; namespace osu.Game.Rulesets.Sentakki.UI { @@ -15,6 +16,6 @@ public SentakkiReplayRecorder(Replay replay) } protected override ReplayFrame HandleFrame(Vector2 mousePosition, List actions, ReplayFrame previousFrame) - => new SentakkiReplayFrame(Time.Current, mousePosition, actions.ToArray()); + => new SentakkiReplayFrame(Time.Current, mousePosition, Array.Empty(), actions.ToArray()); } } From 2cda9756d00fc34c3ee99b488661c90a24264148 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Oct 2020 11:11:09 +0200 Subject: [PATCH 06/13] Use the same ReleaseDelay for touches --- .../Replays/SentakkiAutoGenerator.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs index ebe9991d7..66bfe81d4 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs @@ -118,10 +118,10 @@ private IEnumerable generateActionPoints() { Time = currentObject.StartTime, PointNumber = nextAvailableTouchPoint(), - TouchReplayEvent = new TouchReplayEvent(t.Position, 10, currentObject.StartTime) + TouchReplayEvent = new TouchReplayEvent(t.Position, RELEASE_DELAY, currentObject.StartTime) }; - yield return new TouchUp { Time = endTime + 10, PointNumber = nextAvailableTouchPoint() }; - touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + 10; + yield return new TouchUp { Time = endTime + RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + RELEASE_DELAY; break; case TouchHold _: @@ -129,10 +129,10 @@ private IEnumerable generateActionPoints() { Time = currentObject.StartTime, PointNumber = nextAvailableTouchPoint(), - TouchReplayEvent = new TouchReplayEvent(Vector2.Zero, 10, currentObject.StartTime) + TouchReplayEvent = new TouchReplayEvent(Vector2.Zero, RELEASE_DELAY, currentObject.StartTime) }; - yield return new TouchUp { Time = endTime + 10, PointNumber = nextAvailableTouchPoint() }; - touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + 10; + yield return new TouchUp { Time = endTime + RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + RELEASE_DELAY; break; } } From 8135e51fd71a5664dee00850802126a10ea2fc59 Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Oct 2020 11:38:12 +0200 Subject: [PATCH 07/13] AutoTouch should block user input in touch objects --- .../Objects/Drawables/DrawableSlideNode.cs | 2 ++ osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs | 1 + .../Objects/Drawables/DrawableTouchHold.cs | 2 -- osu.Game.Rulesets.Sentakki/UI/Lane.cs | 1 - 4 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs index 783b5e422..66d272554 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSlideNode.cs @@ -110,6 +110,8 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void Update() { base.Update(); + if (AutoTouch) return; + if (Time.Current >= Slide.HitObject.StartTime) { var touchInput = SentakkiActionInputManager.CurrentState.Touch; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs index c730f4a7f..bbaf5b1eb 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs @@ -100,6 +100,7 @@ public DrawableTouch(Touch hitObject) : base(hitObject) protected override void Update() { base.Update(); + if (AutoTouch) return; int count = 0; var touchInput = SentakkiActionInputManager.CurrentState.Touch; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs index efee87a2e..a2ef7e1ef 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouchHold.cs @@ -127,8 +127,6 @@ protected override void Update() isHitting.Value = Time.Current >= HitObject.StartTime && Time.Current <= (HitObject as IHasDuration)?.EndTime && (AutoTouch || isTouched || ((SentakkiActionInputManager?.PressedActions.Any() ?? false) && IsHovered)); - - Console.WriteLine("SS Centre: " + ScreenSpaceDrawQuad.Centre); } protected override void UpdateStateTransforms(ArmedState state) diff --git a/osu.Game.Rulesets.Sentakki/UI/Lane.cs b/osu.Game.Rulesets.Sentakki/UI/Lane.cs index 2d4a5fe6b..fd70affd2 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Lane.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Lane.cs @@ -60,7 +60,6 @@ protected override void Update() private void handleKeyPress(ValueChangedEvent keys) { - if (sentakkiActionInputManager.ReplayInputHandler != null) return; if (keys.NewValue > keys.OldValue || keys.NewValue == 0) SentakkiActionInputManager.TriggerReleased(SentakkiAction.Key1 + ((Lane)Parent).LaneNumber); From 80b7f4a44bef32e2ebfc52418092d1427ac6bb9c Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Oct 2020 13:29:58 +0200 Subject: [PATCH 08/13] Add visualization for touch input --- .../UI/TestSceneSentakkiTouchPoint.cs | 23 ++++ .../UI/Components/TouchVisualization.cs | 118 +++++++++++++++--- 2 files changed, 123 insertions(+), 18 deletions(-) create mode 100644 osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs diff --git a/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs b/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs new file mode 100644 index 000000000..104d718a6 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs @@ -0,0 +1,23 @@ +using NUnit.Framework; +using osu.Framework.Input; +using osu.Framework.Timing; +using osu.Game.Rulesets.Sentakki.UI.Components; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Sentakki.Tests.UI +{ + [TestFixture] + public class TestSceneSentakkiTouchPoint : OsuGridTestScene + { + public TestSceneSentakkiTouchPoint() : base(5, 2) + { + for (int i = 0; i < 10; ++i) + { + TouchVisualization.TouchPointer tmp; + Cell(i).Add(tmp = new TouchVisualization.TouchPointer((TouchSource)i)); + tmp.Show(); + } + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs index 8555952ee..357100342 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs @@ -1,17 +1,13 @@ -using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; -using osu.Game.Beatmaps; using osu.Game.Graphics; -using osu.Game.Rulesets.Sentakki.Configuration; +using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; -using System; -using osu.Game.Online.API; -using osu.Game.Users; -using osu.Game.Skinning; +using osu.Framework.Input; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Colour; +using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.UI.Components { @@ -30,24 +26,110 @@ public TouchVisualization() { dots = new Container() }; + for (int i = 0; i < 10; ++i) + { + dots.Add(new TouchPointer((TouchSource)i)); + } } protected override void Update() { base.Update(); - dots.Clear(); var touchInput = SentakkiActionInputManager.CurrentState.Touch; - foreach (var points in touchInput.ActiveSources) + List toShow = new List(new bool[10]); + foreach (var point in touchInput.ActiveSources) + { + toShow[(int)point] = true; + dots.Children[(int)point].Position = ToLocalSpace(touchInput.GetTouchPosition(point) ?? Vector2.Zero); + } + + for (int i = 0; i < 10; ++i) + { + if (toShow[i]) + dots.Children[i].Show(); + else + dots.Children[i].Hide(); + } + } + + public class TouchPointer : VisibilityContainer + { + protected override bool StartHidden => true; + public TouchPointer(TouchSource source) + { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Size = new Vector2(50); + InternalChildren = new Drawable[] + { + new SpriteIcon + { + Icon = FontAwesome.Solid.HandPaper, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = ColourInfo.GradientVertical(Color4.White, colourForSource(source, true)) + }, + new SpriteIcon + { + Icon = FontAwesome.Regular.HandPaper, + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colourForSource(source), + }, + new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = ((int)source+1).ToString(), + Font = OsuFont.Torus.With(size: 18,weight: FontWeight.Medium), + Colour = colourForSource(source), + Position = new Vector2(.07f,.175f), + RelativePositionAxes = Axes.Both, + } + }; + } + protected override void PopIn() + { + FinishTransforms(); + this.ScaleTo(1, 50, Easing.OutBack).FadeIn(50); + } + + protected override void PopOut() + { + FinishTransforms(); + this.FadeOut(200).Then().ScaleTo(0); + } + + private Color4 colourForSource(TouchSource source, bool light = false) { - dots.Add(new Circle + OsuColour colours = new OsuColour(); + switch (source) { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(20), - Position = ToLocalSpace(touchInput.GetTouchPosition(points) ?? new Vector2(float.MinValue)), - Colour = Color4.Green, - }); + case TouchSource.Touch1: + return light ? colours.RedLight : colours.Red; + case TouchSource.Touch2: + return light ? colours.PurpleLight : colours.Purple; + case TouchSource.Touch3: + return light ? colours.PinkLight : colours.Pink; + case TouchSource.Touch4: + return light ? colours.BlueLight : colours.BlueDarker; + case TouchSource.Touch5: + return light ? colours.YellowLight : colours.Yellow; + case TouchSource.Touch6: + return light ? colours.GreenLight : colours.Green; + case TouchSource.Touch7: + return light ? colours.Sky : colours.GreySky; + case TouchSource.Touch8: + return light ? colours.Cyan : colours.GreyCyan; + case TouchSource.Touch9: + return light ? colours.Lime : colours.GreyLime; + case TouchSource.Touch10: + return light ? colours.GreyViolet : colours.Violet; + } + return Color4.Blue; } } } From d63324f67b4b32ae1903c9ed8185ece1c961cc8d Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Oct 2020 13:43:39 +0200 Subject: [PATCH 09/13] Make the autoplay touch reactions more "human" --- .../Replays/SentakkiAutoGenerator.cs | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs index 66bfe81d4..c81bf15b3 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs @@ -15,7 +15,9 @@ namespace osu.Game.Rulesets.Sentakki.Replays { public class SentakkiAutoGenerator : AutoGenerator { - public const double RELEASE_DELAY = 20; + public const double KEY_RELEASE_DELAY = 20; + public const double TOUCH_RELEASE_DELAY = 50; + public const double TOUCH_REACT_DELAY = 200; protected Replay Replay; protected List Frames => Replay.Frames; @@ -88,9 +90,9 @@ private IEnumerable generateActionPoints() var nextObjectInColumn = GetNextObject(i) as SentakkiLanedHitObject; // Get the next object that requires pressing the same button bool canDelayKeyUp = nextObjectInColumn == null || - nextObjectInColumn.StartTime > endTime + RELEASE_DELAY; + nextObjectInColumn.StartTime > endTime + KEY_RELEASE_DELAY; - double calculatedDelay = canDelayKeyUp ? RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; + double calculatedDelay = canDelayKeyUp ? KEY_RELEASE_DELAY : (nextObjectInColumn.StartTime - endTime) * 0.9; yield return new KeyDown { Time = currentObject.StartTime, Lane = laned.Lane }; yield return new KeyUp { Time = endTime + calculatedDelay, Lane = laned.Lane }; @@ -107,8 +109,8 @@ private IEnumerable generateActionPoints() TouchReplayEvent = new TouchReplayEvent(slideInfo.SlidePath.Path, slideInfo.Duration - delay, currentObject.StartTime + delay, s.Lane.GetRotationForLane() - 22.5f), PointNumber = nextAvailableTouchPoint() }; - yield return new TouchUp { Time = currentObject.StartTime + slideInfo.Duration, PointNumber = nextAvailableTouchPoint() }; - touchPointInUsedUntil[nextAvailableTouchPoint()] = currentObject.StartTime + slideInfo.Duration; + yield return new TouchUp { Time = currentObject.StartTime + slideInfo.Duration + TOUCH_RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = currentObject.StartTime + slideInfo.Duration + TOUCH_REACT_DELAY; } } break; @@ -118,10 +120,10 @@ private IEnumerable generateActionPoints() { Time = currentObject.StartTime, PointNumber = nextAvailableTouchPoint(), - TouchReplayEvent = new TouchReplayEvent(t.Position, RELEASE_DELAY, currentObject.StartTime) + TouchReplayEvent = new TouchReplayEvent(t.Position, TOUCH_RELEASE_DELAY, currentObject.StartTime) }; - yield return new TouchUp { Time = endTime + RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; - touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + RELEASE_DELAY; + yield return new TouchUp { Time = endTime + TOUCH_RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + TOUCH_REACT_DELAY; break; case TouchHold _: @@ -129,10 +131,10 @@ private IEnumerable generateActionPoints() { Time = currentObject.StartTime, PointNumber = nextAvailableTouchPoint(), - TouchReplayEvent = new TouchReplayEvent(Vector2.Zero, RELEASE_DELAY, currentObject.StartTime) + TouchReplayEvent = new TouchReplayEvent(Vector2.Zero, TOUCH_RELEASE_DELAY, currentObject.StartTime) }; - yield return new TouchUp { Time = endTime + RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; - touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + RELEASE_DELAY; + yield return new TouchUp { Time = endTime + TOUCH_RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; + touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + TOUCH_REACT_DELAY; break; } } From 0401ee29f2144e33603902c509a719032f297c4a Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Mon, 5 Oct 2020 15:22:29 +0200 Subject: [PATCH 10/13] CodeFactor fixes --- .../Objects/TestSceneSlideNote.cs | 1 - .../Replays/SentakkiAutoGenerator.cs | 19 +++--------- .../Replays/SentakkiReplayFrame.cs | 30 ------------------ .../Replays/TouchReplayFrame.cs | 31 +++++++++++++++++++ .../SentakkiExtensions.cs | 4 +-- 5 files changed, 37 insertions(+), 48 deletions(-) create mode 100644 osu.Game.Rulesets.Sentakki/Replays/TouchReplayFrame.cs diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs index d83c6d6b8..9ddecc880 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneSlideNote.cs @@ -36,6 +36,5 @@ protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) }); return beatmap; } - } } diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs index c81bf15b3..d3cf2f972 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiAutoGenerator.cs @@ -10,6 +10,8 @@ using osu.Game.Rulesets.Sentakki.Scoring; using Microsoft.EntityFrameworkCore.Query.Expressions; using System.Diagnostics; +using osu.Game.Rulesets.Objects.Types; +using Microsoft.EntityFrameworkCore.Internal; namespace osu.Game.Rulesets.Sentakki.Replays { @@ -66,7 +68,6 @@ public override Replay Generate() if (Replay.Frames.Count == 0) Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time - 1, new Vector2(-1000), Array.Empty())); - Replay.Frames.Add(new SentakkiReplayFrame(group.First().Time, new Vector2(-1000), TouchReplayEvents.ToList().ToArray(), actions.ToArray())); } @@ -115,23 +116,13 @@ private IEnumerable generateActionPoints() } break; - case Touch t: - yield return new TouchDown - { - Time = currentObject.StartTime, - PointNumber = nextAvailableTouchPoint(), - TouchReplayEvent = new TouchReplayEvent(t.Position, TOUCH_RELEASE_DELAY, currentObject.StartTime) - }; - yield return new TouchUp { Time = endTime + TOUCH_RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; - touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + TOUCH_REACT_DELAY; - break; - + case Touch _: case TouchHold _: yield return new TouchDown { Time = currentObject.StartTime, PointNumber = nextAvailableTouchPoint(), - TouchReplayEvent = new TouchReplayEvent(Vector2.Zero, TOUCH_RELEASE_DELAY, currentObject.StartTime) + TouchReplayEvent = new TouchReplayEvent((currentObject is Touch x) ? x.Position : Vector2.Zero, TOUCH_RELEASE_DELAY, currentObject.StartTime) }; yield return new TouchUp { Time = endTime + TOUCH_RELEASE_DELAY, PointNumber = nextAvailableTouchPoint() }; touchPointInUsedUntil[nextAvailableTouchPoint()] = endTime + TOUCH_REACT_DELAY; @@ -153,7 +144,6 @@ protected override HitObject GetNextObject(int currentIndex) return null; } - private interface IActionPoint { double Time { get; set; } @@ -184,7 +174,6 @@ private struct TouchDown : ITouchActionPoint public double Time { get; set; } public TouchReplayEvent TouchReplayEvent { get; set; } public int PointNumber { get; set; } - } private struct TouchUp : ITouchActionPoint { diff --git a/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs b/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs index 998edc891..8d502b3f8 100644 --- a/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs +++ b/osu.Game.Rulesets.Sentakki/Replays/SentakkiReplayFrame.cs @@ -2,11 +2,8 @@ using osu.Game.Beatmaps; using osu.Game.Replays.Legacy; using osu.Game.Rulesets.Replays; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Replays.Types; -using osu.Framework.Input; using osuTK; -using osu.Framework.Input.Events; namespace osu.Game.Rulesets.Sentakki.Replays { @@ -47,31 +44,4 @@ public LegacyReplayFrame ToLegacy(IBeatmap beatmap) return new LegacyReplayFrame(Time, Position.X, Position.Y, state); } } - public class TouchReplayEvent - { - public TouchReplayEvent(Vector2 Position, double Duration, double startTime, float rotation = 0) - { - MovementPath = new SliderPath(new PathControlPoint[]{ - new PathControlPoint(Position) - }); - this.Duration = Duration; - StartTime = startTime; - Rotation = rotation; - } - - public TouchReplayEvent(SliderPath path, double Duration, double startTime, float rotation = 0) - { - MovementPath = path; - this.Duration = Duration; - StartTime = startTime; - Rotation = rotation; - } - - - public SliderPath MovementPath; - public double Duration; - public double StartTime; - public float Rotation = 0; - - } } diff --git a/osu.Game.Rulesets.Sentakki/Replays/TouchReplayFrame.cs b/osu.Game.Rulesets.Sentakki/Replays/TouchReplayFrame.cs new file mode 100644 index 000000000..0c890b9e6 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Replays/TouchReplayFrame.cs @@ -0,0 +1,31 @@ +using osu.Game.Rulesets.Objects; +using osuTK; + +namespace osu.Game.Rulesets.Sentakki.Replays +{ + public class TouchReplayEvent + { + public TouchReplayEvent(Vector2 Position, double Duration, double startTime, float rotation = 0) + { + MovementPath = new SliderPath(new PathControlPoint[]{ + new PathControlPoint(Position) + }); + this.Duration = Duration; + StartTime = startTime; + Rotation = rotation; + } + + public TouchReplayEvent(SliderPath path, double Duration, double startTime, float rotation = 0) + { + MovementPath = path; + this.Duration = Duration; + StartTime = startTime; + Rotation = rotation; + } + + public SliderPath MovementPath; + public double Duration; + public double StartTime; + public float Rotation = 0; + } +} diff --git a/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs b/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs index be672b2a6..5ce2c1a4a 100644 --- a/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs +++ b/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs @@ -76,8 +76,8 @@ public static Vector2 RotatePointAroundOrigin(Vector2 point, Vector2 origin, flo point.Y -= origin.Y; Vector2 ret; - ret.X = point.X * MathF.Cos(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle)); - ret.Y = point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle)) + point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle)); + ret.X = (point.X * MathF.Cos(MathUtils.DegreesToRadians(angle))) + (point.Y * MathF.Sin(MathUtils.DegreesToRadians(angle))); + ret.Y = (point.X * -MathF.Sin(MathUtils.DegreesToRadians(angle))) + (point.Y * MathF.Cos(MathUtils.DegreesToRadians(angle))); ret.X += origin.X; ret.Y += origin.Y; From 03060560dde688e6eb6f95c68cb5c40c92f6f02b Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Thu, 28 Jan 2021 16:53:19 +0100 Subject: [PATCH 11/13] Partially rework touch visualization --- .../UI/TestSceneSentakkiTouchPoint.cs | 9 +- .../UI/Components/TouchVisualization.cs | 147 ++++++++++-------- .../UI/SentakkiPlayfield.cs | 3 +- 3 files changed, 90 insertions(+), 69 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs b/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs index 104d718a6..7969d2cf4 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneSentakkiTouchPoint.cs @@ -1,21 +1,24 @@ using NUnit.Framework; +using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Timing; using osu.Game.Rulesets.Sentakki.UI.Components; using osu.Game.Screens.Play; using osu.Game.Tests.Visual; +using osuTK; namespace osu.Game.Rulesets.Sentakki.Tests.UI { [TestFixture] public class TestSceneSentakkiTouchPoint : OsuGridTestScene { - public TestSceneSentakkiTouchPoint() : base(5, 2) + public TestSceneSentakkiTouchPoint() : base(1, 2) { - for (int i = 0; i < 10; ++i) + for (int i = 0; i < 2; ++i) { TouchVisualization.TouchPointer tmp; - Cell(i).Add(tmp = new TouchVisualization.TouchPointer((TouchSource)i)); + int j = i; + Cell(i).Add(tmp = new TouchVisualization.TouchPointer().With(d => d.Scale = new Vector2(i == 0 ? -1 : 1, 1))); tmp.Show(); } } diff --git a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs index 357100342..fd3940489 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs @@ -8,6 +8,9 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Colour; using System.Collections.Generic; +using osu.Framework.Graphics.Pooling; +using osu.Framework.Allocation; +using System; namespace osu.Game.Rulesets.Sentakki.UI.Components { @@ -16,47 +19,88 @@ public class TouchVisualization : CompositeDrawable private SentakkiInputManager sentakkiActionInputManager; internal SentakkiInputManager SentakkiActionInputManager => sentakkiActionInputManager ??= GetContainingInputManager() as SentakkiInputManager; - private readonly Container dots; + private DrawablePool pointerPool; + + + private int leftCount; + private int rightCount; + + public Dictionary InUsePointers = new Dictionary(); + public TouchVisualization() { + Anchor = Anchor.Centre; + Origin = Anchor.Centre; RelativeSizeAxes = Axes.Both; AlwaysPresent = true; - InternalChildren = new Drawable[] - { - dots = new Container() - }; - for (int i = 0; i < 10; ++i) - { - dots.Add(new TouchPointer((TouchSource)i)); - } } + [BackgroundDependencyLoader] + private void load() + { + AddInternal(pointerPool = new DrawablePool(2)); + } + + protected override void Update() { base.Update(); var touchInput = SentakkiActionInputManager.CurrentState.Touch; - List toShow = new List(new bool[10]); foreach (var point in touchInput.ActiveSources) { - toShow[(int)point] = true; - dots.Children[(int)point].Position = ToLocalSpace(touchInput.GetTouchPosition(point) ?? Vector2.Zero); + Vector2 newPos = ToLocalSpace(touchInput.GetTouchPosition(point) ?? Vector2.Zero) - OriginPosition; + if (!InUsePointers.TryGetValue(point, out TouchPointer pointer)) + { + bool useLeftHand = false; + + if (newPos.X < 0) + { + if (leftCount <= rightCount) + useLeftHand = true; + } + else + { + if (leftCount < rightCount) + useLeftHand = true; + } + + if (useLeftHand) ++leftCount; + else ++rightCount; + Console.WriteLine(leftCount + ", " + rightCount); + + AddInternal(pointer = pointerPool.Get()); + pointer.Scale = new Vector2(useLeftHand ? -1 : 1, 1); + InUsePointers[point] = pointer; + } + pointer.Position = newPos; } - for (int i = 0; i < 10; ++i) + foreach (var pair in InUsePointers) { - if (toShow[i]) - dots.Children[i].Show(); - else - dots.Children[i].Hide(); + if (Time.Current - pair.Value.LastActiveTime >= 50 && pair.Value.InUse) + pair.Value.FinishUsage(); + else if (!pair.Value.IsInUse) + { + InUsePointers.Remove(pair.Key); + if (pair.Value.LeftHand) --leftCount; + else --rightCount; + } } } - public class TouchPointer : VisibilityContainer + public class TouchPointer : PoolableDrawable { - protected override bool StartHidden => true; - public TouchPointer(TouchSource source) + public override bool RemoveCompletedTransforms => false; + + public double LastActiveTime; + public bool LeftHand => Scale.X == -1; + + public bool InUse; + + [BackgroundDependencyLoader] + private void load() { Anchor = Anchor.Centre; Origin = Anchor.Centre; @@ -69,7 +113,7 @@ public TouchPointer(TouchSource source) RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = ColourInfo.GradientVertical(Color4.White, colourForSource(source, true)) + Colour = ColourInfo.GradientVertical(Color4.White, Color4.Gray) }, new SpriteIcon { @@ -77,59 +121,32 @@ public TouchPointer(TouchSource source) RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colourForSource(source), + Colour = Color4.Gray, }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Text = ((int)source+1).ToString(), - Font = OsuFont.Torus.With(size: 18,weight: FontWeight.Medium), - Colour = colourForSource(source), - Position = new Vector2(.07f,.175f), - RelativePositionAxes = Axes.Both, - } }; } - protected override void PopIn() + + public new Vector2 Position { - FinishTransforms(); - this.ScaleTo(1, 50, Easing.OutBack).FadeIn(50); + get => base.Position; + set + { + base.Position = value; + LastActiveTime = Time.Current; + } } - - protected override void PopOut() + protected override void PrepareForUse() { - FinishTransforms(); - this.FadeOut(200).Then().ScaleTo(0); + this.FadeIn(); + LastActiveTime = Time.Current; + LifetimeStart = Time.Current; + InUse = true; } - private Color4 colourForSource(TouchSource source, bool light = false) + public void FinishUsage() { - OsuColour colours = new OsuColour(); - switch (source) - { - case TouchSource.Touch1: - return light ? colours.RedLight : colours.Red; - case TouchSource.Touch2: - return light ? colours.PurpleLight : colours.Purple; - case TouchSource.Touch3: - return light ? colours.PinkLight : colours.Pink; - case TouchSource.Touch4: - return light ? colours.BlueLight : colours.BlueDarker; - case TouchSource.Touch5: - return light ? colours.YellowLight : colours.Yellow; - case TouchSource.Touch6: - return light ? colours.GreenLight : colours.Green; - case TouchSource.Touch7: - return light ? colours.Sky : colours.GreySky; - case TouchSource.Touch8: - return light ? colours.Cyan : colours.GreyCyan; - case TouchSource.Touch9: - return light ? colours.Lime : colours.GreyLime; - case TouchSource.Touch10: - return light ? colours.GreyViolet : colours.Violet; - } - return Color4.Blue; + this.FadeOut(50).Expire(false); + InUse = false; } } } diff --git a/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs b/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs index a9d7a8a8e..09146f626 100644 --- a/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs +++ b/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs @@ -64,7 +64,7 @@ public SentakkiPlayfield() { RelativeSizeAxes = Axes.Both, }, - new TouchVisualization() + }); AddNested(lanedPlayfield); NewResult += onNewResult; @@ -81,6 +81,7 @@ private void load(DrawableSentakkiRuleset drawableRuleset, SentakkiRulesetConfig RegisterPool(2); RegisterPool(8); + AddInternal(new TouchVisualization()); } protected override HitObjectLifetimeEntry CreateLifetimeEntry(HitObject hitObject) => new SentakkiHitObjectLifetimeEntry(hitObject, sentakkiRulesetConfig, drawableSentakkiRuleset); From e39504a02945b271a7c9c0399c2a3cef6954a9ad Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Thu, 28 Jan 2021 17:00:29 +0100 Subject: [PATCH 12/13] Temporarily disable handedness... --- .../UI/Components/TouchVisualization.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs index fd3940489..7ba3c6c93 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs @@ -83,9 +83,9 @@ protected override void Update() pair.Value.FinishUsage(); else if (!pair.Value.IsInUse) { - InUsePointers.Remove(pair.Key); + InUsePointers.Remove(pair.Key);/* if (pair.Value.LeftHand) --leftCount; - else --rightCount; + else --rightCount; */ } } } From a571327b35231e9dd4b7849a9608f1c1da81f1ad Mon Sep 17 00:00:00 2001 From: Derrick Timmermans Date: Thu, 28 Jan 2021 17:08:38 +0100 Subject: [PATCH 13/13] Actually disable handedness --- .../UI/Components/TouchVisualization.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs index 7ba3c6c93..9aa6462a1 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Components/TouchVisualization.cs @@ -22,8 +22,8 @@ public class TouchVisualization : CompositeDrawable private DrawablePool pointerPool; - private int leftCount; - private int rightCount; + //private int leftCount; + //private int rightCount; public Dictionary InUsePointers = new Dictionary(); @@ -55,7 +55,7 @@ protected override void Update() { bool useLeftHand = false; - if (newPos.X < 0) + /* if (newPos.X < 0) { if (leftCount <= rightCount) useLeftHand = true; @@ -69,7 +69,7 @@ protected override void Update() if (useLeftHand) ++leftCount; else ++rightCount; Console.WriteLine(leftCount + ", " + rightCount); - + */ AddInternal(pointer = pointerPool.Get()); pointer.Scale = new Vector2(useLeftHand ? -1 : 1, 1); InUsePointers[point] = pointer;