diff --git a/.grenrc b/.grenrc index 38ca32277..ded610533 100644 --- a/.grenrc +++ b/.grenrc @@ -10,7 +10,7 @@ "wontfix" ], "template": { - "issue": "- {{name}} | [{{text}}]({{url}}) by [{{user_login}}]({{user_url}})", + "issue": "- {{name}} | [{{text}}]({{url}})", "group": "\n### {{heading}}\n", "changelogTitle": "" }, diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs index f0e5902b2..4ab1f740b 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneBreakNote.cs @@ -35,9 +35,6 @@ private void testSingle(bool auto = false) { IsBreak = true, StartTime = Time.Current + 1000, - Position = new Vector2(0, -66f), - Angle = 0, - EndPosition = new Vector2(0, -296.5f), }; circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs index 7fb6d3d4f..5454241c7 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneHoldNote.cs @@ -45,9 +45,6 @@ private void testSingle(double duration, bool auto = false) { StartTime = Time.Current + 1000, EndTime = Time.Current + 1000 + duration, - Position = new Vector2(0, -66), - EndPosition = new Vector2(0, -296.5f), - Angle = 0f, }; circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs index ba72248d9..442219038 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTapNote.cs @@ -34,9 +34,6 @@ private void testSingle(bool auto = false) var circle = new Tap { StartTime = Time.Current + 1000, - Position = new Vector2(0, -66f), - Angle = 0, - EndPosition = new Vector2(0, -296.5f), }; circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); diff --git a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs index 5f9f54fd3..4b606e088 100644 --- a/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs +++ b/osu.Game.Rulesets.Sentakki.Tests/Objects/TestSceneTouchHold.cs @@ -34,7 +34,6 @@ private void testSingle(bool auto = false) { StartTime = Time.Current + 1000, Duration = 5000, - Position = new Vector2(0, 0) }; circle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { }); diff --git a/osu.Game.Rulesets.Sentakki.Tests/Statistics/TestSceneJudgementChart.cs b/osu.Game.Rulesets.Sentakki.Tests/Statistics/TestSceneJudgementChart.cs new file mode 100644 index 000000000..72f1afb05 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki.Tests/Statistics/TestSceneJudgementChart.cs @@ -0,0 +1,61 @@ +using NUnit.Framework; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Sentakki.Statistics; +using osu.Game.Rulesets.Sentakki.Objects; +using osu.Game.Tests.Visual; +using System.Collections.Generic; + +namespace osu.Game.Rulesets.Sentakki.Tests.Statistics +{ + [TestFixture] + public class TestSceneJudgementChart : OsuTestScene + { + private List testevents = new List + { + // Tap + new HitEvent(0,HitResult.Perfect,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Meh,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Meh,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Meh,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Miss,new Tap(),new Tap(), null), + new HitEvent(0,HitResult.Miss,new Tap(),new Tap(), null), + // Holds + new HitEvent(0,HitResult.Perfect,new Hold.HoldHead(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Hold.HoldTail(),new Tap(), null), + // Touch + new HitEvent(0,HitResult.Good,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Good,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Meh,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Meh,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Meh,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Miss,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Miss,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Touch(),new Tap(), null), + new HitEvent(0,HitResult.Perfect,new Touch(),new Tap(), null), + // Breaks + new HitEvent(0,HitResult.Perfect,new Tap(){IsBreak = true},new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(){IsBreak = true},new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(){IsBreak = true},new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(){IsBreak = true},new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(){IsBreak = true},new Tap(), null), + new HitEvent(0,HitResult.Good,new Tap(){IsBreak = true},new Tap(), null), + new HitEvent(0,HitResult.Meh,new Tap(){IsBreak = true},new Tap(), null), + }; + public TestSceneJudgementChart() + { + Add(new JudgementChart(testevents)); + } + } +} diff --git a/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneResumeOverlay.cs b/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneResumeOverlay.cs new file mode 100644 index 000000000..dcde3c5a4 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki.Tests/UI/TestSceneResumeOverlay.cs @@ -0,0 +1,31 @@ +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Game.Rulesets.Sentakki.UI; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Sentakki.Tests.UI +{ + public class TestSceneResumeOverlay : OsuTestScene + { + public TestSceneResumeOverlay() + { + CursorContainer cursor; + ResumeOverlay resume; + + Children = new Drawable[] + { + cursor = new CursorContainer(), + resume = new SentakkiResumeOverlay + { + GameplayCursor = cursor + } + }; + + AddStep("Show ResumeOverlay", () => resume.Show()); + AddAssert("Is overlay shown?", () => resume.State.Value == Visibility.Visible); + AddUntilStep("Wait for countdown to end", () => resume.State.Value == Visibility.Hidden); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs index a65fd5052..cfe1f50b1 100644 --- a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs +++ b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiBeatmapConverter.cs @@ -49,7 +49,7 @@ protected override IEnumerable ConvertHitObject(HitObject ori Vector2 newPos = (original as IHasPosition)?.Position ?? Vector2.Zero; newPos.Y = 384 - newPos.Y; - int path = newPos.GetDegreesFromPosition(CENTRE_POINT).GetNotePathFromDegrees(); + int lane = newPos.GetDegreesFromPosition(CENTRE_POINT).GetNoteLaneFromDegrees(); List objects = new List(); if (EnabledExperiments.Value.HasFlag(ConversionExperiments.patternv2)) @@ -65,7 +65,7 @@ protected override IEnumerable ConvertHitObject(HitObject ori switch (original) { case IHasPathWithRepeats _: - objects.AddRange(Conversions.CreateHoldNote(original, path, beatmap, random, EnabledExperiments.Value)); + objects.AddRange(Conversions.CreateHoldNote(original, lane, beatmap, random, EnabledExperiments.Value)); break; case IHasDuration _: @@ -74,9 +74,9 @@ protected override IEnumerable ConvertHitObject(HitObject ori default: if (EnabledExperiments.Value.HasFlag(ConversionExperiments.touch) && (random2.Next() % 10 == 0)) - objects.AddRange(Conversions.CreateTouchNote(original, path, random, EnabledExperiments.Value)); + objects.AddRange(Conversions.CreateTouchNote(original, lane, random, EnabledExperiments.Value)); else - objects.AddRange(Conversions.CreateTapNote(original, path, random, EnabledExperiments.Value)); + objects.AddRange(Conversions.CreateTapNote(original, lane, random, EnabledExperiments.Value)); break; } diff --git a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiNoteConversions.cs b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiNoteConversions.cs index 205ed3ba3..5d381da03 100644 --- a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiNoteConversions.cs +++ b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiNoteConversions.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Sentakki.Beatmaps { public static class Conversions { - public static List CreateTapNote(HitObject original, int path, Random rng, ConversionExperiments experimental = ConversionExperiments.none) + public static List CreateTapNote(HitObject original, int lane, Random rng, ConversionExperiments experimental = ConversionExperiments.none) { List notes = new List(); bool strong = original.Samples.Any(s => s.Name == HitSampleInfo.HIT_FINISH); @@ -26,24 +26,20 @@ public static List CreateTapNote(HitObject original, int path notes.Add(new Tap { IsBreak = strong, - Angle = path.GetAngleFromPath(), + Lane = lane, Samples = original.Samples, - StartTime = original.StartTime, - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, path), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, path), + StartTime = original.StartTime }); if (twin && experimental.HasFlag(ConversionExperiments.twins)) { - int newPath = path; - while (path == newPath) newPath = rng.Next(0, 8); + int newPath = lane; + while (lane == newPath) newPath = rng.Next(0, 8); notes.Add(new Tap { IsBreak = strong, - Angle = newPath.GetAngleFromPath(), + Lane = newPath, Samples = original.Samples, - StartTime = original.StartTime, - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, newPath), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, newPath), + StartTime = original.StartTime }); foreach (var note in notes) note.HasTwin = true; @@ -52,7 +48,7 @@ public static List CreateTapNote(HitObject original, int path return notes; } - public static List CreateTouchNote(HitObject original, int path, Random rng, ConversionExperiments experimental = ConversionExperiments.none) + public static List CreateTouchNote(HitObject original, int lane, Random rng, ConversionExperiments experimental = ConversionExperiments.none) { Vector2 newPos = (original as IHasPosition)?.Position ?? Vector2.Zero; newPos.Y = 384 - newPos.Y - 192; @@ -62,7 +58,7 @@ public static List CreateTouchNote(HitObject original, int pa { Samples = original.Samples, StartTime = original.StartTime, - Position = newPos, + Position = newPos }}; return notes; @@ -71,13 +67,12 @@ public static List CreateTouchNote(HitObject original, int pa public static SentakkiHitObject CreateTouchHold(HitObject original) => new TouchHold { - Position = Vector2.Zero, StartTime = original.StartTime, EndTime = (original as IHasDuration).EndTime, Samples = original.Samples, }; - public static List CreateHoldNote(HitObject original, int path, IBeatmap beatmap, Random rng, ConversionExperiments experimental = ConversionExperiments.none) + public static List CreateHoldNote(HitObject original, int lane, IBeatmap beatmap, Random rng, ConversionExperiments experimental = ConversionExperiments.none) { var curveData = original as IHasPathWithRepeats; @@ -88,36 +83,32 @@ public static List CreateHoldNote(HitObject original, int pat notes.Add(new Hold { IsBreak = strong, - Angle = path.GetAngleFromPath(), + Lane = lane, NodeSamples = curveData.NodeSamples, StartTime = original.StartTime, - EndTime = original.GetEndTime(), - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, path), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, path), + EndTime = original.GetEndTime() }); if (experimental.HasFlag(ConversionExperiments.twins)) { if (twin) { - int newPath = path; - while (path == newPath) newPath = rng.Next(0, 8); + int newLane = lane; + while (lane == newLane) newLane = rng.Next(0, 8); notes.Add(new Hold { IsBreak = strong, - Angle = newPath.GetAngleFromPath(), + Lane = newLane, NodeSamples = curveData.NodeSamples, StartTime = original.StartTime, EndTime = original.GetEndTime(), - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, newPath), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, newPath), }); foreach (var note in notes) note.HasTwin = true; } else { - var taps = CreateTapFromTicks(original, path, beatmap, rng); + var taps = CreateTapFromTicks(original, lane, beatmap, rng); if (taps.Any()) notes.AddRange(taps); } @@ -126,7 +117,7 @@ public static List CreateHoldNote(HitObject original, int pat return notes; } - public static List CreateTapFromTicks(HitObject original, int path, IBeatmap beatmap, Random rng) + public static List CreateTapFromTicks(HitObject original, int lane, IBeatmap beatmap, Random rng) { var curve = original as IHasPathWithRepeats; double spanDuration = curve.Duration / (curve.RepeatCount + 1); @@ -151,8 +142,8 @@ public static List CreateTapFromTicks(HitObject original, int foreach (var e in SliderEventGenerator.Generate(original.StartTime, spanDuration, velocity, tickDistance, curve.Path.Distance, curve.RepeatCount + 1, legacyLastTickOffset, CancellationToken.None)) { - int newPath = path; - while (newPath == path) newPath = rng.Next(0, 8); + int newLane = lane; + while (newLane == lane) newLane = rng.Next(0, 8); switch (e.Type) { @@ -160,11 +151,9 @@ public static List CreateTapFromTicks(HitObject original, int case SliderEventType.Repeat: hitObjects.Add(new Tap { - Angle = newPath.GetAngleFromPath(), + Lane = newLane, Samples = getTickSamples(original.Samples), StartTime = e.Time, - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, newPath), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, newPath), }); break; } diff --git a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiPatternGenerator.cs b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiPatternGenerator.cs index cd84b95da..1e69fa48e 100644 --- a/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiPatternGenerator.cs +++ b/osu.Game.Rulesets.Sentakki/Beatmaps/SentakkiPatternGenerator.cs @@ -30,17 +30,17 @@ public SentakkiPatternGenerator(IBeatmap beatmap) rng = new Random(seed); } - //The patterns will generate the note path to be used based on the current offset + //The patterns will generate the note lane to be used based on the current offset // argument list is (offset, diff) private List> patternlist => new List>{ - //Stream pattern, path difference determined by offset2 + //Stream pattern, lane difference determined by offset2 (twin)=> { if(twin) return offset + 4; else offset+=offset2; return offset; }, // Back and forth, works better with longer combos - // Path difference determined by offset2, but will make sure offset2 is never 0. + // Lane difference determined by offset2, but will make sure offset2 is never 0. (twin)=>{ offset2 = offset2 == 0 ? 1 : offset2; offset+=offset2; @@ -53,7 +53,7 @@ public SentakkiPatternGenerator(IBeatmap beatmap) private int offset = 0; private int offset2 = 0; - private int getNewPath(bool twin = false) => patternlist[currentPattern].Invoke(twin); + private int getNewLane(bool twin = false) => patternlist[currentPattern].Invoke(twin); public void CreateNewPattern() { @@ -120,21 +120,19 @@ public IEnumerable GenerateNewNote(HitObject original) // Individual note generation code, because it's cleaner private SentakkiHitObject createHoldNote(HitObject original, bool twin = false, bool isBreak = false) { - int notePath = getNewPath(twin); + int noteLane = getNewLane(twin); return new Hold { IsBreak = isBreak, - Angle = notePath.GetAngleFromPath(), + Lane = noteLane, NodeSamples = (original as IHasPathWithRepeats).NodeSamples, StartTime = original.StartTime, - EndTime = original.GetEndTime(), - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, notePath), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, notePath), + EndTime = original.GetEndTime() }; } private IEnumerable createTapsFromTicks(HitObject original) { - int notePath = getNewPath(true); + int noteLane = getNewLane(true); var curve = original as IHasPathWithRepeats; double spanDuration = curve.Duration / (curve.RepeatCount + 1); @@ -164,16 +162,14 @@ private IEnumerable createTapsFromTicks(HitObject original) case SliderEventType.Repeat: yield return new Tap { - Angle = notePath.GetAngleFromPath(), + Lane = noteLane, Samples = original.Samples.Select(s => new HitSampleInfo { Bank = s.Bank, Name = @"slidertick", Volume = s.Volume }).ToList(), - StartTime = e.Time, - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, notePath), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, notePath), + StartTime = e.Time }; break; } @@ -182,15 +178,13 @@ private IEnumerable createTapsFromTicks(HitObject original) private SentakkiHitObject createTapNote(HitObject original, bool twin = false, bool isBreak = false) { - int notePath = getNewPath(twin); + int noteLane = getNewLane(twin); return new Tap { IsBreak = isBreak, - Angle = notePath.GetAngleFromPath(), + Lane = noteLane, Samples = original.Samples, StartTime = original.StartTime, - EndPosition = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.INTERSECTDISTANCE, notePath), - Position = SentakkiExtensions.GetPathPosition(SentakkiPlayfield.NOTESTARTDISTANCE, notePath), }; } diff --git a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/BlueprintPiece.cs b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/BlueprintPiece.cs index f594c4b6e..ff2a64514 100644 --- a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/BlueprintPiece.cs +++ b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/BlueprintPiece.cs @@ -1,6 +1,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; using osu.Game.Rulesets.Sentakki.Objects; +using osu.Game.Rulesets.Sentakki.UI; namespace osu.Game.Rulesets.Sentakki.Edit.Blueprints { @@ -22,7 +23,7 @@ public BlueprintPiece() /// The to reference properties from. public virtual void UpdateFrom(T hitObject) { - Position = hitObject.EndPosition; + Position = SentakkiExtensions.GetCircularPosition(SentakkiPlayfield.INTERSECTDISTANCE, hitObject.Lane.GetRotationForLane()); } } } diff --git a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/Components/HoldSelection.cs b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/Components/HoldSelection.cs index feec6d751..1fef51ce0 100644 --- a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/Components/HoldSelection.cs +++ b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/Components/HoldSelection.cs @@ -6,6 +6,7 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Sentakki.Objects; using osu.Game.Rulesets.Sentakki.Objects.Drawables; +using osu.Game.Rulesets.Sentakki.UI; using osuTK; using osuTK.Graphics; @@ -85,8 +86,8 @@ private void load(OsuColour colours) public override void UpdateFrom(Hold hitObject) { //base.UpdateFrom(hitObject); - Rotation = hitObject.Angle; - notebody.Position = hitObject.EndPosition; + Rotation = hitObject.Lane.GetRotationForLane(); + notebody.Position = new Vector2(0, -SentakkiPlayfield.INTERSECTDISTANCE + 40); } public void UpdateFrom(DrawableHold drawableHold) diff --git a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/HoldPlacementBlueprint.cs b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/HoldPlacementBlueprint.cs index 40888efb1..b64c53d60 100644 --- a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/HoldPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Holds/HoldPlacementBlueprint.cs @@ -65,11 +65,7 @@ public override void UpdatePosition(SnapResult result) newPos.Y = 600 - newPos.Y; newPos -= new Vector2(300); - var path = newPos.GetDegreesFromPosition(Vector2.Zero).GetNotePathFromDegrees(); - var angle = path.GetAngleFromPath(); - - HitObject.Angle = angle; - HitObject.EndPosition = new Vector2(0, -SentakkiPlayfield.INTERSECTDISTANCE + 40); + HitObject.Lane = newPos.GetDegreesFromPosition(Vector2.Zero).GetNoteLaneFromDegrees(); } else { diff --git a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/Components/TapPiece.cs b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/Components/TapPiece.cs index 3d44f606f..f04baeeff 100644 --- a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/Components/TapPiece.cs +++ b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/Components/TapPiece.cs @@ -59,7 +59,7 @@ public override void UpdateFrom(Tap hitObject) public void UpdateFrom(DrawableTap drawableTap) { - Position = drawableTap.CirclePiece.Position; + Position = SentakkiExtensions.GetCircularPosition(-drawableTap.CirclePiece.Position.Y, drawableTap.HitObject.Lane.GetRotationForLane()); } } } diff --git a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/TapPlacementBlueprint.cs b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/TapPlacementBlueprint.cs index f1be4901c..62db8c4a4 100644 --- a/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/TapPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Sentakki/Edit/Blueprints/Taps/TapPlacementBlueprint.cs @@ -52,13 +52,7 @@ public override void UpdatePosition(SnapResult result) newPos.Y = 600 - newPos.Y; newPos -= new Vector2(300); - var path = newPos.GetDegreesFromPosition(Vector2.Zero).GetNotePathFromDegrees(); - var angle = path.GetAngleFromPath(); - - HitObject.Angle = angle; - - HitObject.Position = SentakkiExtensions.GetCircularPosition(SentakkiPlayfield.NOTESTARTDISTANCE, angle); - HitObject.EndPosition = SentakkiExtensions.GetCircularPosition(SentakkiPlayfield.INTERSECTDISTANCE, angle); + HitObject.Lane = newPos.GetDegreesFromPosition(Vector2.Zero).GetNoteLaneFromDegrees(); ; } } } diff --git a/osu.Game.Rulesets.Sentakki/Edit/SentakkiSelectionHandler.cs b/osu.Game.Rulesets.Sentakki/Edit/SentakkiSelectionHandler.cs index acd7dff64..024b474a0 100644 --- a/osu.Game.Rulesets.Sentakki/Edit/SentakkiSelectionHandler.cs +++ b/osu.Game.Rulesets.Sentakki/Edit/SentakkiSelectionHandler.cs @@ -37,20 +37,9 @@ public override bool HandleMovement(MoveSelectionEvent moveEvent) break; } case Tap _: - { - var path = newPos.GetDegreesFromPosition(Vector2.Zero).GetNotePathFromDegrees(); - var angle = path.GetAngleFromPath(); - h.Position = SentakkiExtensions.GetCircularPosition(SentakkiPlayfield.NOTESTARTDISTANCE, angle); - h.EndPosition = SentakkiExtensions.GetCircularPosition(SentakkiPlayfield.INTERSECTDISTANCE, angle); - h.Angle = angle; - break; - } case Hold _: { - var path = newPos.GetDegreesFromPosition(Vector2.Zero).GetNotePathFromDegrees(); - var angle = path.GetAngleFromPath(); - h.EndPosition = new Vector2(0, -SentakkiPlayfield.INTERSECTDISTANCE + 40); - h.Angle = angle; + h.Lane = newPos.GetDegreesFromPosition(Vector2.Zero).GetNoteLaneFromDegrees(); break; } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs index 23f52ad88..629ffe0b2 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableHold.cs @@ -81,10 +81,14 @@ public DrawableHold(Hold hitObject) isHitting.Value = false; NoteBody.Glow.FadeOut(100); }, - NoteAngle = HitObject.Angle } }); - HitObject.BindableAngle.BindValueChanged(angle => Rotation = angle.NewValue, true); + + hitObject.LaneBindable.BindValueChanged(r => + { + Rotation = r.NewValue.GetRotationForLane(); + HitArea.NotePath = r.NewValue; + }, true); } protected override void AddNestedHitObject(DrawableHitObject hitObject) @@ -114,7 +118,7 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) { switch (hitObject) { - case HoldTail _: + case Hold.HoldTail _: return new DrawableHoldTail(this) { Anchor = Anchor.TopCentre, @@ -122,7 +126,7 @@ protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) AccentColour = { BindTarget = AccentColour } }; - case Tap _: + case Hold.HoldHead _: return new DrawableHoldHead(this) { Anchor = Anchor.TopCentre, diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs index d0a641c99..d7d2abd75 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableSentakkiHitObject.cs @@ -31,7 +31,7 @@ public class DrawableSentakkiHitObject : DrawableHitObject // Used for the animation update protected readonly Bindable AnimationDuration = new Bindable(1000); - protected override float SamplePlaybackPosition => (HitObject.EndPosition.X + SentakkiPlayfield.INTERSECTDISTANCE) / (SentakkiPlayfield.INTERSECTDISTANCE * 2); + protected override float SamplePlaybackPosition => (SentakkiExtensions.GetPositionAlongLane(SentakkiPlayfield.INTERSECTDISTANCE, HitObject.Lane).X / (SentakkiPlayfield.INTERSECTDISTANCE * 2)) + .5f; public SentakkiAction[] HitActions { get; set; } = new[] { SentakkiAction.Button1, diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs index 3820822c5..980bb2299 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTap.cs @@ -5,6 +5,8 @@ using osu.Game.Rulesets.Sentakki.Objects.Drawables.Pieces; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Sentakki.UI; +using osu.Framework.Utils; using osuTK; using osuTK.Graphics; using System; @@ -32,7 +34,7 @@ public DrawableTap(SentakkiHitObject hitObject) CirclePiece = new TapCircle() { Scale = new Vector2(0f), - Position = HitObject.Position + Position = new Vector2(0, -SentakkiPlayfield.NOTESTARTDISTANCE) }, HitArea = new HitReceptor() { @@ -44,15 +46,13 @@ public DrawableTap(SentakkiHitObject hitObject) UpdateResult(true); return true; }, - Position = hitObject.EndPosition, + Position = new Vector2(0, -SentakkiPlayfield.INTERSECTDISTANCE), }, }); - - HitObject.BindableAngle.BindValueChanged(angle => + hitObject.LaneBindable.BindValueChanged(r => { - HitObjectLine.Rotation = angle.NewValue; - HitArea.NoteAngle = angle.NewValue; - HitArea.Position = hitObject.EndPosition; + Rotation = r.NewValue.GetRotationForLane(); + HitArea.NotePath = r.NewValue; }, true); } @@ -81,7 +81,7 @@ protected override void Update() // Calculate position float moveAmount = Math.Clamp((float)((currentProg - animTime) / animTime), 0, 1); - CirclePiece.Position = HitObject.Position + ((HitObject.EndPosition - HitObject.Position) * moveAmount); + CirclePiece.Y = (float)Interpolation.Lerp(-SentakkiPlayfield.NOTESTARTDISTANCE, -SentakkiPlayfield.INTERSECTDISTANCE, moveAmount); // Handle hidden and fadeIn modifications if (IsHidden) @@ -137,12 +137,10 @@ protected override void UpdateStateTransforms(ArmedState state) break; case ArmedState.Miss: - var c = HitObject.Angle + 90; - var d = c * (float)(Math.PI / 180); CirclePiece.ScaleTo(0.5f, time_fade_miss, Easing.InCubic) .FadeColour(Color4.Red, time_fade_miss, Easing.OutQuint) - .MoveToOffset(new Vector2(-(100 * (float)Math.Cos(d)), -(100 * (float)Math.Sin(d))), time_fade_hit, Easing.OutCubic) + .MoveToOffset(new Vector2(0, -100), time_fade_hit, Easing.OutCubic) .FadeOut(time_fade_miss); this.ScaleTo(1f, time_fade_miss).Expire(); diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs index aca8adc14..4870d91f9 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/DrawableTouch.cs @@ -23,7 +23,7 @@ public class DrawableTouch : DrawableSentakkiHitObject, IDrawableHitObjectWithPr public Drawable ProxiedLayer => this; - protected override float SamplePlaybackPosition => (HitObject.Position.X + SentakkiPlayfield.INTERSECTDISTANCE) / (SentakkiPlayfield.INTERSECTDISTANCE * 2); + protected override float SamplePlaybackPosition => ((HitObject as Touch).Position.X / (SentakkiPlayfield.INTERSECTDISTANCE * 2)) + .5f; protected override double InitialLifetimeOffset => 6000; @@ -42,7 +42,7 @@ public class DrawableTouch : DrawableSentakkiHitObject, IDrawableHitObjectWithPr private SentakkiInputManager sentakkiActionInputManager; internal SentakkiInputManager SentakkiActionInputManager => sentakkiActionInputManager ??= GetContainingInputManager() as SentakkiInputManager; - public DrawableTouch(SentakkiHitObject hitObject) : base(hitObject) + public DrawableTouch(Touch hitObject) : base(hitObject) { Size = new Vector2(80); Position = hitObject.Position; @@ -102,7 +102,7 @@ private void load(SentakkiRulesetConfigManager sentakkiConfigs) } // Easing functions for manual use. - private readonly DefaultEasingFunction inOutBack = new DefaultEasingFunction(Easing.InOutBack); + private readonly DefaultEasingFunction outSine = new DefaultEasingFunction(Easing.OutSine); private readonly DefaultEasingFunction inQuint = new DefaultEasingFunction(Easing.InQuint); protected override void Update() @@ -112,21 +112,21 @@ protected override void Update() if (Result.HasResult) return; double fadeIn = AnimationDuration.Value * GameplaySpeed; - double moveTo = 500 * GameplaySpeed; + double moveTo = HitObject.HitWindows.WindowFor(HitResult.Meh) * 2 * GameplaySpeed; double animStart = HitObject.StartTime - fadeIn - moveTo; double currentProg = Clock.CurrentTime - animStart; // Calculate initial entry animation float fadeAmount = Math.Clamp((float)(currentProg / fadeIn), 0, 1); - Alpha = fadeAmount * (float)inOutBack.ApplyEasing(fadeAmount); - Scale = new Vector2(fadeAmount * (float)inOutBack.ApplyEasing(fadeAmount)); + Alpha = fadeAmount * (float)outSine.ApplyEasing(fadeAmount); + Scale = new Vector2(fadeAmount * (float)outSine.ApplyEasing(fadeAmount)); // Calculate position float moveAmount = Math.Clamp((float)((currentProg - fadeIn) / moveTo), 0, 1); // Used to simplify this crazy arse manual animating - float moveAnimFormula(float originalValue) => (float)(originalValue - (originalValue * moveAmount * inQuint.ApplyEasing(moveAmount))); + float moveAnimFormula(float originalValue) => (float)(originalValue - (originalValue * inQuint.ApplyEasing(moveAmount))); blob1.Position = new Vector2(moveAnimFormula(40), 0); blob2.Position = new Vector2(moveAnimFormula(-40), 0); @@ -134,7 +134,7 @@ protected override void Update() blob4.Position = new Vector2(0, moveAnimFormula(-40)); // Used to simplify this crazy arse manual animating - float sizeAnimFormula() => (float)(.5 + .5 * moveAmount * inQuint.ApplyEasing(moveAmount)); + float sizeAnimFormula() => (float)(.5 + .5 * inQuint.ApplyEasing(moveAmount)); blob1.Scale = new Vector2(sizeAnimFormula()); blob2.Scale = new Vector2(sizeAnimFormula()); @@ -188,8 +188,8 @@ protected override void CheckForResult(bool userTriggered, double timeOffset) var result = HitObject.HitWindows.ResultFor(timeOffset); if (timeOffset < 0 && result <= HitResult.Miss) return; - if (result >= HitResult.Meh && timeOffset < 0) - result = HitResult.Perfect; + if (result >= HitResult.Meh && result < HitResult.Great && timeOffset < 0) + result = HitResult.Great; ApplyResult(r => r.Type = result); } diff --git a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/HitReceptor.cs b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/HitReceptor.cs index 843ea4148..5907a94d7 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/HitReceptor.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Drawables/Pieces/HitReceptor.cs @@ -21,10 +21,10 @@ public class HitReceptor : CircularContainer, IKeyBindingHandler public Func Hit; public Action Release; - public float NoteAngle = -1; + public int? NotePath = null; public bool HoverAction() { - if (!SentakkiActionInputManager.CurrentAngles.Contains(NoteAngle)) + if (!NotePath.HasValue || !SentakkiActionInputManager.CurrentPath.Contains(NotePath.Value)) { if (SentakkiActionInputManager.PressedActions.Any(action => OnPressed(action))) actions.AddRange(SentakkiActionInputManager.PressedActions.Except(actions)); @@ -44,7 +44,8 @@ public virtual bool OnPressed(SentakkiAction action) { if (IsHovered) { - SentakkiActionInputManager.CurrentAngles.Add(NoteAngle); + if (NotePath.HasValue) + SentakkiActionInputManager.CurrentPath.Add(NotePath.Value); actions.Add(action); return Hit?.Invoke() ?? false; } @@ -57,13 +58,15 @@ public void OnReleased(SentakkiAction action) if (!actions.Any()) { Release?.Invoke(); - SentakkiActionInputManager.CurrentAngles.Remove(NoteAngle); + if (NotePath.HasValue) + SentakkiActionInputManager.CurrentPath.Remove(NotePath.Value); } } protected override void OnHoverLost(HoverLostEvent e) { - SentakkiActionInputManager.CurrentAngles.Remove(NoteAngle); + if (NotePath.HasValue) + SentakkiActionInputManager.CurrentPath.Remove(NotePath.Value); if (actions.Any()) { actions.Clear(); diff --git a/osu.Game.Rulesets.Sentakki/Objects/Hold.cs b/osu.Game.Rulesets.Sentakki/Objects/Hold.cs index 61c5fcd5f..0a1eb3d96 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Hold.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Hold.cs @@ -63,29 +63,18 @@ public override double StartTime } } - public override float Angle + public override int Lane { - get => base.Angle; + get => base.Lane; set { - base.Angle = value; - Head.Angle = value; - Tail.Angle = value; + base.Lane = value; + Head.Lane = value; + Tail.Lane = value; } } - public override Vector2 EndPosition - { - get => base.EndPosition; - set - { - base.EndPosition = value; - Head.EndPosition = value; - Tail.EndPosition = value; - } - } - - public readonly Tap Head = new Tap(); + public readonly HoldHead Head = new HoldHead(); public readonly HoldTail Tail = new HoldTail(); @@ -100,5 +89,8 @@ protected override void CreateNestedHitObjects() public override Judgement CreateJudgement() => new IgnoreJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; + + public class HoldHead : SentakkiHitObject { } + public class HoldTail : SentakkiHitObject { } } } diff --git a/osu.Game.Rulesets.Sentakki/Objects/HoldTail.cs b/osu.Game.Rulesets.Sentakki/Objects/HoldTail.cs deleted file mode 100644 index cb9fea782..000000000 --- a/osu.Game.Rulesets.Sentakki/Objects/HoldTail.cs +++ /dev/null @@ -1,7 +0,0 @@ -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Sentakki.Judgements; - -namespace osu.Game.Rulesets.Sentakki.Objects -{ - public class HoldTail : Tap { } -} diff --git a/osu.Game.Rulesets.Sentakki/Objects/SentakkiHitObject.cs b/osu.Game.Rulesets.Sentakki/Objects/SentakkiHitObject.cs index 37a7a7a45..abdab7ddc 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/SentakkiHitObject.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/SentakkiHitObject.cs @@ -19,17 +19,16 @@ public abstract class SentakkiHitObject : HitObject, IHasPosition public override Judgement CreateJudgement() => IsBreak ? new SentakkiBreakJudgement() : new SentakkiJudgement(); public virtual Color4 NoteColor => IsBreak ? Color4.OrangeRed : (HasTwin ? Color4.Gold : Color4Extensions.FromHex("ff0064")); - public virtual Vector2 EndPosition { get; set; } - public virtual float Angle + + public readonly BindableInt LaneBindable = new BindableInt(0); + public virtual int Lane { - get => BindableAngle.Value; - set => BindableAngle.Value = value; + get => LaneBindable.Value; + set => LaneBindable.Value = value; } - public readonly Bindable BindableAngle = new Bindable(0); - - public Vector2 Position { get; set; } - + // This section is required just so editor actually starts + public Vector2 Position { get; set; } = Vector2.Zero; public float X => Position.X; public float Y => Position.Y; diff --git a/osu.Game.Rulesets.Sentakki/Objects/Touch.cs b/osu.Game.Rulesets.Sentakki/Objects/Touch.cs index 7272f5bdb..dab217ecc 100644 --- a/osu.Game.Rulesets.Sentakki/Objects/Touch.cs +++ b/osu.Game.Rulesets.Sentakki/Objects/Touch.cs @@ -1,18 +1,17 @@ using osu.Game.Rulesets.Sentakki.Scoring; using osu.Game.Rulesets.Scoring; using osuTK.Graphics; +using osuTK; namespace osu.Game.Rulesets.Sentakki.Objects { public class Touch : SentakkiHitObject { + // Currently to avoid conflict issues + //public new Vector2 Position { get; set; } + public override bool IsBreak => false; public override Color4 NoteColor => HasTwin ? Color4.Gold : Color4.Cyan; - - public override float Angle => 0; - - // This is not actually used during the result check, since all valid hits result in a perfect judgement - // The only reason that it's here is so that hits show on the accuracy meter at the side. - protected override HitWindows CreateHitWindows() => new SentakkiHitWindows(); + protected override HitWindows CreateHitWindows() => new SentakkiTouchHitWindows(); } } diff --git a/osu.Game.Rulesets.Sentakki/Resources/Samples/Gameplay/Taka.wav b/osu.Game.Rulesets.Sentakki/Resources/Samples/Gameplay/Taka.wav new file mode 100644 index 000000000..e04d318d9 Binary files /dev/null and b/osu.Game.Rulesets.Sentakki/Resources/Samples/Gameplay/Taka.wav differ diff --git a/osu.Game.Rulesets.Sentakki/Scoring/SentakkiHitWindows.cs b/osu.Game.Rulesets.Sentakki/Scoring/SentakkiHitWindows.cs index f745020d4..aff3ce7dd 100644 --- a/osu.Game.Rulesets.Sentakki/Scoring/SentakkiHitWindows.cs +++ b/osu.Game.Rulesets.Sentakki/Scoring/SentakkiHitWindows.cs @@ -18,5 +18,13 @@ public override bool IsHitResultAllowed(HitResult result) return false; } } + + protected override DifficultyRange[] GetRanges() => new DifficultyRange[]{ + new DifficultyRange(HitResult.Miss, 144, 144, 144), + new DifficultyRange(HitResult.Meh, 144, 144, 144), + new DifficultyRange(HitResult.Good, 96, 96, 96), + new DifficultyRange(HitResult.Great, 48, 48, 48), + new DifficultyRange(HitResult.Perfect, 16, 16, 16) + }; } } diff --git a/osu.Game.Rulesets.Sentakki/Scoring/SentakkiTouchHitWindows.cs b/osu.Game.Rulesets.Sentakki/Scoring/SentakkiTouchHitWindows.cs new file mode 100644 index 000000000..b402815db --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Scoring/SentakkiTouchHitWindows.cs @@ -0,0 +1,15 @@ +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Sentakki.Scoring +{ + public class SentakkiTouchHitWindows : SentakkiHitWindows + { + protected override DifficultyRange[] GetRanges() => new DifficultyRange[]{ + new DifficultyRange(HitResult.Miss, 288, 288, 288), + new DifficultyRange(HitResult.Meh, 288, 288, 288), + new DifficultyRange(HitResult.Good, 240, 240, 240), + new DifficultyRange(HitResult.Great, 192, 192, 192), + new DifficultyRange(HitResult.Perfect, 144, 144, 144) + }; + } +} diff --git a/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs b/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs index 038843901..293114d38 100644 --- a/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs +++ b/osu.Game.Rulesets.Sentakki/SentakkiExtensions.cs @@ -6,13 +6,13 @@ namespace osu.Game.Rulesets.Sentakki { public static class SentakkiExtensions { - public static float GetAngleFromPath(this int path) + public static float GetRotationForLane(this int lane) { - while (path < 0) path += 8; - path %= 8; - return SentakkiPlayfield.PATHANGLES[path]; + while (lane < 0) lane += 8; + lane %= 8; + return SentakkiPlayfield.LANEANGLES[lane]; } - public static Vector2 GetPathPosition(float distance, int path) => GetCircularPosition(distance, path.GetAngleFromPath()); + public static Vector2 GetPositionAlongLane(float distance, int lane) => GetCircularPosition(distance, lane.GetRotationForLane()); public static Vector2 GetCircularPosition(float distance, float angle) { @@ -21,15 +21,15 @@ public static Vector2 GetCircularPosition(float distance, float angle) public static float GetDegreesFromPosition(this Vector2 target, Vector2 self) => (float)MathHelper.RadiansToDegrees(Math.Atan2(target.X - self.X, target.Y - self.Y)); - public static int GetNotePathFromDegrees(this float degrees) + public static int GetNoteLaneFromDegrees(this float degrees) { if (degrees < 0) degrees += 360; if (degrees >= 360) degrees %= 360; int result = 0; - for (int i = 0; i < SentakkiPlayfield.PATHANGLES.Length; ++i) + for (int i = 0; i < SentakkiPlayfield.LANEANGLES.Length; ++i) { - if (SentakkiPlayfield.PATHANGLES[i] - degrees >= -22.5f && SentakkiPlayfield.PATHANGLES[i] - degrees <= 22.5f) + if (SentakkiPlayfield.LANEANGLES[i] - degrees >= -22.5f && SentakkiPlayfield.LANEANGLES[i] - degrees <= 22.5f) result = i; } return result; diff --git a/osu.Game.Rulesets.Sentakki/SentakkiInputManager.cs b/osu.Game.Rulesets.Sentakki/SentakkiInputManager.cs index 3a9195750..54516e686 100644 --- a/osu.Game.Rulesets.Sentakki/SentakkiInputManager.cs +++ b/osu.Game.Rulesets.Sentakki/SentakkiInputManager.cs @@ -40,7 +40,7 @@ public SentakkiInputManager(RulesetInfo ruleset) { } - public List CurrentAngles = new List(); + public List CurrentPath = new List(); } public enum SentakkiAction diff --git a/osu.Game.Rulesets.Sentakki/SentakkiRuleset.cs b/osu.Game.Rulesets.Sentakki/SentakkiRuleset.cs index 51d19dfeb..e4252d8e7 100644 --- a/osu.Game.Rulesets.Sentakki/SentakkiRuleset.cs +++ b/osu.Game.Rulesets.Sentakki/SentakkiRuleset.cs @@ -4,7 +4,9 @@ using osu.Framework.Input.Bindings; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Scoring; using osu.Game.Overlays.Settings; +using osu.Game.Screens.Ranking.Statistics; using osu.Game.Rulesets.Configuration; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Sentakki.Beatmaps; @@ -12,6 +14,7 @@ using osu.Game.Rulesets.Sentakki.Mods; using osu.Game.Rulesets.Sentakki.Replays; using osu.Game.Rulesets.Sentakki.Scoring; +using osu.Game.Rulesets.Sentakki.Statistics; using osu.Game.Rulesets.Sentakki.UI; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Replays.Types; @@ -20,6 +23,7 @@ using System.Collections.Generic; using osu.Game.Rulesets.Sentakki.Edit; using osu.Game.Rulesets.Edit; +using osuTK; namespace osu.Game.Rulesets.Sentakki { @@ -97,6 +101,32 @@ public override IEnumerable GetDefaultKeyBindings(int variant = 0) = new KeyBinding(InputKey.MouseRight, SentakkiAction.Button2), }; + public override StatisticRow[] CreateStatisticsForScore(ScoreInfo score, IBeatmap playableBeatmap) => new[] + { + new StatisticRow + { + Columns = new[] + { + new StatisticItem("Timing Distribution", new HitEventTimingDistributionGraph(score.HitEvents) + { + RelativeSizeAxes = Axes.X, + Height = 250 + }) + } + }, + new StatisticRow + { + Columns = new[] + { + new StatisticItem("Judgement Distribution", new JudgementChart(score.HitEvents) + { + RelativeSizeAxes = Axes.X, + Size = new Vector2(1, 250) + }), + } + } + }; + public override Drawable CreateIcon() => new Sprite { Texture = new TextureStore(new TextureLoaderStore(CreateResourceStore()), false).Get("Textures/Icon2"), diff --git a/osu.Game.Rulesets.Sentakki/Statistics/JudgementChart.cs b/osu.Game.Rulesets.Sentakki/Statistics/JudgementChart.cs new file mode 100644 index 000000000..d0bc0e0d6 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/Statistics/JudgementChart.cs @@ -0,0 +1,252 @@ +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Rulesets.Sentakki.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Graphics.UserInterface; +using osuTK.Graphics; +using osuTK; + +namespace osu.Game.Rulesets.Sentakki.Statistics +{ + public class JudgementChart : CompositeDrawable + { + private const double entry_animation_duration = 150; + private const double bar_fill_duration = 3000; + public JudgementChart(List hitEvents) + { + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + Size = new Vector2(500, 250); + AddRangeInternal(new Drawable[]{ + new NoteEntry + { + ObjectName = "Tap", + HitEvents = hitEvents.Where(e=> e.HitObject is Tap && !(e.HitObject as SentakkiHitObject).IsBreak).ToList(), + Position = new Vector2(0, 0), + InitialLifetimeOffset = entry_animation_duration * 0 + }, + new NoteEntry + { + ObjectName = "Hold", + HitEvents = hitEvents.Where(e => (e.HitObject is Hold.HoldHead || e.HitObject is Hold.HoldTail) && !(e.HitObject as SentakkiHitObject).IsBreak).ToList(), + Position = new Vector2(0, .16f), + InitialLifetimeOffset = entry_animation_duration * 1 + }, + new NoteEntry + { + ObjectName = "Slide", + HitEvents = new List(), + Position = new Vector2(0, .32f), + InitialLifetimeOffset = entry_animation_duration * 2 + }, + new NoteEntry + { + ObjectName = "Touch", + HitEvents = hitEvents.Where(e => e.HitObject is Touch).ToList(), + Position = new Vector2(0, .48f), + InitialLifetimeOffset = entry_animation_duration * 3 + }, + new NoteEntry + { + ObjectName = "Touch Hold", + HitEvents = hitEvents.Where(e => e.HitObject is TouchHold).ToList(), + Position = new Vector2(0, .64f), + InitialLifetimeOffset = entry_animation_duration * 4 + }, + new NoteEntry + { + ObjectName = "Break", + HitEvents = hitEvents.Where(e => (e.HitObject as SentakkiHitObject).IsBreak).ToList(), + Position = new Vector2(0, .80f), + InitialLifetimeOffset = entry_animation_duration * 5 + }, + }); + } + public class NoteEntry : Container + { + public double InitialLifetimeOffset = 0; + private Container progressBox; + private RollingCounter noteCounter; + + public string ObjectName = "Object"; + public List HitEvents; + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + float MehCount = 0; + float GoodCount = 0; + float GreatCount = 0; + + foreach (var e in HitEvents) + { + switch (e.Result) + { + case HitResult.Perfect: + case HitResult.Great: + ++GreatCount; + goto case HitResult.Good; + case HitResult.Good: + ++GoodCount; + goto case HitResult.Meh; + case HitResult.Meh: + ++MehCount; + break; + } + } + + Color4 textColour = HitEvents.Count == 0 ? Color4Extensions.FromHex("bcbcbc") : (GreatCount == HitEvents.Count) ? Color4.White : Color4Extensions.FromHex("#3c5394"); + Color4 boxColour = HitEvents.Count == 0 ? Color4Extensions.FromHex("808080") : (GreatCount == HitEvents.Count) ? Color4Extensions.FromHex("fda908") : Color4Extensions.FromHex("#DCE9F9"); + Color4 borderColour = HitEvents.Count == 0 ? Color4Extensions.FromHex("536277") : (GreatCount == HitEvents.Count) ? Color4Extensions.FromHex("fda908") : Color4Extensions.FromHex("#98b8df"); + Color4 numberColour = (GreatCount == HitEvents.Count && HitEvents.Count > 0) ? Color4.White : Color4Extensions.FromHex("#3c5394"); + + Anchor = Anchor.TopCentre; + Origin = Anchor.TopCentre; + RelativePositionAxes = Axes.Both; + RelativeSizeAxes = Axes.Both; + Size = new Vector2(1, .16f); + Scale = new Vector2(1, 0); + Alpha = 0; + Masking = true; + BorderThickness = 2; + BorderColour = borderColour; + CornerRadius = 5; + CornerExponent = 2.5f; + AlwaysPresent = true; + + InternalChildren = new Drawable[]{ + new Box { + RelativeSizeAxes = Axes.Both, + Colour = boxColour, + }, + new Container { // Left + RelativeSizeAxes = Axes.Both, + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + Size = new Vector2(.33f, 1), + Child = new OsuSpriteText + { + Colour = textColour, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = ObjectName.ToUpper(), + Font = OsuFont.Torus.With(size: 20, weight: FontWeight.Bold) + } + }, + progressBox = new Container { // Centre + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Size = new Vector2(.34f, .8f), + CornerRadius = 5, + CornerExponent = 2.5f, + Masking = true, + BorderThickness = 2, + BorderColour = Color4.Black, + Children = new Drawable[]{ + new Box{ + RelativeSizeAxes = Axes.Both, + Colour = (HitEvents.Count ==0) ? Color4Extensions.FromHex("343434"):Color4.DarkGray, + } + } + }, + new Container { // Right + RelativeSizeAxes = Axes.Both, + Origin = Anchor.CentreRight, + Anchor = Anchor.CentreRight, + Size = new Vector2(.33f, 1), + Child = noteCounter = new TotalNoteCounter + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = numberColour, + Current = { Value = 0 }, + } + }, + }; + + progressBox.AddRange(new Drawable[]{ + new ChartBar(HitResult.Meh, MehCount/HitEvents.Count){ + InitialLifetimeOffset = InitialLifetimeOffset + }, + new ChartBar(HitResult.Good, GoodCount/HitEvents.Count){ + InitialLifetimeOffset = InitialLifetimeOffset + }, + new ChartBar(HitResult.Great, GreatCount/HitEvents.Count){ + InitialLifetimeOffset = InitialLifetimeOffset + }, + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + ScheduleAfterChildren(() => + { + using (BeginDelayedSequence(InitialLifetimeOffset, true)) + { + this.ScaleTo(1, entry_animation_duration, Easing.OutBack).FadeIn(); + noteCounter.Current.Value = HitEvents.Count; + } + }); + } + + public class TotalNoteCounter : RollingCounter + { + protected override double RollingDuration => bar_fill_duration; + + protected override Easing RollingEasing => Easing.OutPow10; + + public TotalNoteCounter() + { + DisplayedCountSpriteText.Anchor = Anchor.Centre; + DisplayedCountSpriteText.Origin = Anchor.Centre; + DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold); + } + + protected override string FormatCount(long count) => count.ToString("N0"); + + public override void Increment(long amount) + => Current.Value += amount; + } + + private class ChartBar : Container + { + public double InitialLifetimeOffset = 0; + + private HitResult result; + + public ChartBar(HitResult result, float progress) + { + this.result = result; + RelativeSizeAxes = Axes.Both; + Size = new Vector2(float.IsNaN(progress) ? 0 : progress, 1); + Scale = new Vector2(0, 1); + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours) + { + Add(new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.ForHitResult(result) + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + this.Delay(InitialLifetimeOffset).ScaleTo(1, bar_fill_duration, Easing.OutPow10); + } + } + } + } +} diff --git a/osu.Game.Rulesets.Sentakki/UI/Components/SentakkiRing.cs b/osu.Game.Rulesets.Sentakki/UI/Components/SentakkiRing.cs index f7205d91a..b09843997 100644 --- a/osu.Game.Rulesets.Sentakki/UI/Components/SentakkiRing.cs +++ b/osu.Game.Rulesets.Sentakki/UI/Components/SentakkiRing.cs @@ -97,7 +97,7 @@ public SentakkiRing() }; // Add dots to the actual ring - foreach (float pathAngle in SentakkiPlayfield.PATHANGLES) + foreach (float pathAngle in SentakkiPlayfield.LANEANGLES) { AddInternal(new CircularContainer { diff --git a/osu.Game.Rulesets.Sentakki/UI/DrawableSentakkiRuleset.cs b/osu.Game.Rulesets.Sentakki/UI/DrawableSentakkiRuleset.cs index 0155390a8..77f367647 100644 --- a/osu.Game.Rulesets.Sentakki/UI/DrawableSentakkiRuleset.cs +++ b/osu.Game.Rulesets.Sentakki/UI/DrawableSentakkiRuleset.cs @@ -11,6 +11,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play; using System.Collections.Generic; namespace osu.Game.Rulesets.Sentakki.UI @@ -62,6 +63,8 @@ public override DrawableHitObject CreateDrawableRepresentatio return null; } + protected override ResumeOverlay CreateResumeOverlay() => new SentakkiResumeOverlay(); + protected override PassThroughInputManager CreateInputManager() => new SentakkiInputManager(Ruleset?.RulesetInfo); } } diff --git a/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs b/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs index efaa2e3d9..614b49b5b 100644 --- a/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs +++ b/osu.Game.Rulesets.Sentakki/UI/SentakkiPlayfield.cs @@ -31,17 +31,17 @@ public class SentakkiPlayfield : Playfield, IRequireHighFrequencyMousePosition public static readonly float INTERSECTDISTANCE = 296.5f; public static readonly float NOTESTARTDISTANCE = 66f; - public static readonly float[] PATHANGLES = - { - 22.5f, - 67.5f, - 112.5f, - 157.5f, - 202.5f, - 247.5f, - 292.5f, - 337.5f - }; + public static readonly float[] LANEANGLES = + { + 22.5f, + 67.5f, + 112.5f, + 157.5f, + 202.5f, + 247.5f, + 292.5f, + 337.5f + }; // Touch notes always appear above other notes, regardless of start time private readonly TouchNoteProxyContainer touchNoteContainer; @@ -105,8 +105,6 @@ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) var sentakkiObj = (DrawableSentakkiHitObject)judgedObject; - var b = sentakkiObj.HitObject.Angle + 90; - var a = b *= (float)(Math.PI / 180); DrawableSentakkiJudgement explosion; switch (judgedObject) { @@ -131,8 +129,8 @@ private void onNewResult(DrawableHitObject judgedObject, JudgementResult result) { Origin = Anchor.Centre, Anchor = Anchor.Centre, - Position = new Vector2(-(240 * (float)Math.Cos(a)), -(240 * (float)Math.Sin(a))), - Rotation = sentakkiObj.HitObject.Angle, + Position = SentakkiExtensions.GetPositionAlongLane(240, sentakkiObj.HitObject.Lane), + Rotation = sentakkiObj.HitObject.Lane.GetRotationForLane(), }; break; } diff --git a/osu.Game.Rulesets.Sentakki/UI/SentakkiResumeOverlay.cs b/osu.Game.Rulesets.Sentakki/UI/SentakkiResumeOverlay.cs new file mode 100644 index 000000000..f77c61336 --- /dev/null +++ b/osu.Game.Rulesets.Sentakki/UI/SentakkiResumeOverlay.cs @@ -0,0 +1,91 @@ +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Skinning; +using osu.Game.Audio; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Screens.Play; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osuTK.Graphics; +using System; + +namespace osu.Game.Rulesets.Sentakki.UI +{ + public class SentakkiResumeOverlay : ResumeOverlay + { + protected override string Message => "Get ready!"; + + private double timePassed = 3500; + private Bindable tickCount = new Bindable(4); + + private OsuSpriteText counterText; + private readonly SkinnableSound countSound; + + private SentakkiCursorContainer localCursorContainer; + + public override CursorContainer LocalCursor => State.Value == Visibility.Visible ? localCursorContainer : null; + + public SentakkiResumeOverlay() + { + Origin = Anchor.Centre; + Anchor = Anchor.Centre; + RelativeSizeAxes = Axes.Both; + FillMode = FillMode.Fill; + Children = new Drawable[]{ + counterText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Text = "", + Font = OsuFont.Torus.With(size: 50), + Colour = Color4.White, + }, + countSound = new SkinnableSound(new SampleInfo("Taka")) + }; + tickCount.BindValueChanged( + ticks => + { + counterText.Text = (ticks.NewValue == 4) ? "" : ticks.NewValue.ToString(); + if (ticks.NewValue % 4 != 0) countSound?.Play(); + if (ticks.NewValue <= 0) Resume(); + } + ); + } + + protected override void Update() + { + base.Update(); + timePassed -= Clock.ElapsedFrameTime; + tickCount.Value = (int)Math.Ceiling(timePassed / 1000); + } + + protected override void PopIn() + { + base.PopIn(); + + // Reset the countdown + timePassed = 3500; + + GameplayCursor.ActiveCursor.Hide(); + + if (localCursorContainer == null) + { + Add(localCursorContainer = new SentakkiCursorContainer()); + localCursorContainer.MoveTo(GameplayCursor.ActiveCursor.Position); + } + } + + protected override void PopOut() + { + base.PopOut(); + + if (localCursorContainer != null && GameplayCursor?.ActiveCursor != null) + GameplayCursor.ActiveCursor.Position = localCursorContainer.Position; + + localCursorContainer?.Expire(); + localCursorContainer = null; + GameplayCursor?.ActiveCursor?.Show(); + } + } +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Sentakki/osu.Game.Rulesets.Sentakki.csproj b/osu.Game.Rulesets.Sentakki/osu.Game.Rulesets.Sentakki.csproj index 083676a0a..2670d13b0 100644 --- a/osu.Game.Rulesets.Sentakki/osu.Game.Rulesets.Sentakki.csproj +++ b/osu.Game.Rulesets.Sentakki/osu.Game.Rulesets.Sentakki.csproj @@ -10,6 +10,6 @@ osu.Game.Rulesets.Sentakki - + \ No newline at end of file