diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs index 611daad642..981da050a3 100644 --- a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSlider.cs @@ -1,12 +1,13 @@ // Copyright (c) 2007-2017 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System.Collections.Generic; +using OpenTK; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Game.Modes.Objects.Drawables; using osu.Game.Modes.Osu.Objects.Drawables.Pieces; -using OpenTK; -using osu.Framework.Input; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Modes.Osu.Objects.Drawables { @@ -22,6 +23,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables SliderBall ball; SliderBouncer bouncer1, bouncer2; + SliderTicksRenderer ticks; public DrawableSlider(Slider s) : base(s) { @@ -34,6 +36,13 @@ namespace osu.Game.Modes.Osu.Objects.Drawables Position = s.StackedPosition, PathWidth = s.Scale * 64, }, + ticks = new SliderTicksRenderer + { + Position = s.StackedPosition, + StartTime = s.StartTime, + RepeatDuration = s.Curve.Length / s.Velocity, + Ticks = s.Ticks, + }, bouncer1 = new SliderBouncer(s, false) { Position = s.Curve.PositionAt(1), @@ -96,6 +105,7 @@ namespace osu.Game.Modes.Osu.Objects.Drawables initialCircle.Position = slider.Curve.PositionAt(progress); components.ForEach(c => c.UpdateProgress(progress, repeat)); + ticks.ShouldHit = ball.Tracking; } protected override void CheckJudgement(bool userTriggered) @@ -105,8 +115,22 @@ namespace osu.Game.Modes.Osu.Objects.Drawables if (!userTriggered && Time.Current >= HitObject.EndTime) { - j.Score = sc.Score; - j.Result = sc.Result; + var ticksCount = ticks.Children.Count() + 1; + var ticksHit = ticks.Children.Count(t => t.Judgement.Result == HitResult.Hit); + if (sc.Result == HitResult.Hit) + ticksHit++; + + var hitFraction = (double)ticksHit / ticksCount; + if (hitFraction == 1 && sc.Score == OsuScoreResult.Hit300) + j.Score = OsuScoreResult.Hit300; + else if (hitFraction >= 0.5 && sc.Score >= OsuScoreResult.Hit100) + j.Score = OsuScoreResult.Hit100; + else if (hitFraction > 0) + j.Score = OsuScoreResult.Hit50; + else + j.Score = OsuScoreResult.Miss; + + j.Result = j.Score != OsuScoreResult.Miss ? HitResult.Hit : HitResult.Miss; } } diff --git a/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs new file mode 100644 index 0000000000..cd5f33a4e6 --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -0,0 +1,92 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using OpenTK; +using OpenTK.Graphics; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Transformations; +using osu.Game.Modes.Objects.Drawables; +using System; + +namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces +{ + public class DrawableSliderTick : DrawableOsuHitObject + { + private SliderTick sliderTick; + + public double FadeInTime; + public double FadeOutTime; + + public bool ShouldHit; + + public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick) + { + this.sliderTick = sliderTick; + + Size = new Vector2(16) * sliderTick.Scale; + + Masking = true; + CornerRadius = Size.X / 2; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + BorderThickness = 2; + BorderColour = Color4.White; + + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = sliderTick.Colour, + Alpha = 0.3f, + } + }; + } + + protected override void CheckJudgement(bool userTriggered) + { + if (Judgement.TimeOffset >= 0) + Judgement.Result = ShouldHit ? HitResult.Hit : HitResult.Miss; + } + + protected override void UpdatePreemptState() + { + var animIn = Math.Min(150, sliderTick.StartTime - FadeInTime); + + ScaleTo(0.5f); + ScaleTo(1.2f, animIn); + FadeIn(animIn); + + Delay(animIn); + ScaleTo(1, 150, EasingTypes.Out); + + Delay(-animIn); + } + + protected override void UpdateState(ArmedState state) + { + if (!IsLoaded) return; + + base.UpdateState(state); + + switch (state) + { + case ArmedState.Idle: + Delay(FadeOutTime - sliderTick.StartTime); + FadeOut(); + break; + case ArmedState.Miss: + FadeTo(0.6f); + Delay(FadeOutTime - sliderTick.StartTime); + FadeOut(); + break; + case ArmedState.Hit: + FadeOut(); + break; + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs new file mode 100644 index 0000000000..82fa99d00a --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/Drawables/SliderTicksRenderer.cs @@ -0,0 +1,74 @@ +// Copyright (c) 2007-2017 ppy Pty Ltd . +// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE + +using osu.Framework.Graphics.Containers; +using System.Collections.Generic; + +namespace osu.Game.Modes.Osu.Objects.Drawables.Pieces +{ + public class SliderTicksRenderer : Container + { + private double startTime; + public double StartTime + { + get { return startTime; } + set + { + startTime = value; + update(); + } + } + + private double repeatDuration; + public double RepeatDuration + { + get { return repeatDuration; } + set + { + repeatDuration = value; + update(); + } + } + + private IEnumerable ticks; + public IEnumerable Ticks + { + get { return ticks; } + set + { + ticks = value; + update(); + } + } + + public bool ShouldHit + { + set + { + foreach (var tick in Children) + tick.ShouldHit = value; + } + } + + private void update() + { + Clear(); + if (ticks == null || repeatDuration == 0) + return; + + foreach (var tick in ticks) + { + var repeatStartTime = startTime + tick.RepeatIndex * repeatDuration; + var fadeInTime = repeatStartTime + (tick.StartTime - repeatStartTime) / 2 - (tick.RepeatIndex == 0 ? DrawableOsuHitObject.TIME_FADEIN : DrawableOsuHitObject.TIME_FADEIN / 2); + var fadeOutTime = repeatStartTime + repeatDuration; + + Add(new DrawableSliderTick(tick) + { + FadeInTime = fadeInTime, + FadeOutTime = fadeOutTime, + Position = tick.Position, + }); + } + } + } +} \ No newline at end of file diff --git a/osu.Game.Modes.Osu/Objects/Slider.cs b/osu.Game.Modes.Osu/Objects/Slider.cs index 85ee83a7f8..6f97ccf654 100644 --- a/osu.Game.Modes.Osu/Objects/Slider.cs +++ b/osu.Game.Modes.Osu/Objects/Slider.cs @@ -3,6 +3,9 @@ using osu.Game.Beatmaps; using OpenTK; +using System.Collections.Generic; +using System; +using osu.Game.Beatmaps.Samples; namespace osu.Game.Modes.Osu.Objects { @@ -25,17 +28,66 @@ namespace osu.Game.Modes.Osu.Objects } public double Velocity; + public double TickDistance; public override void SetDefaultsFromBeatmap(Beatmap beatmap) { base.SetDefaultsFromBeatmap(beatmap); - Velocity = 100 / beatmap.BeatLengthAt(StartTime, true) * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier; + var baseDifficulty = beatmap.BeatmapInfo.BaseDifficulty; + + var startBeatLength = beatmap.BeatLengthAt(StartTime); + var multipliedStartBeatLength = beatmap.BeatLengthAt(StartTime, true); + + Velocity = 100 / multipliedStartBeatLength * baseDifficulty.SliderMultiplier; + TickDistance = (100 * baseDifficulty.SliderMultiplier) / baseDifficulty.SliderTickRate / (multipliedStartBeatLength / startBeatLength); } public int RepeatCount; public SliderCurve Curve; + + public IEnumerable Ticks + { + get + { + var length = Curve.Length; + var tickDistance = Math.Min(TickDistance, length); + var repeatDuration = length / Velocity; + + var minDistanceFromEnd = Velocity * 0.01; + + for (var repeat = 0; repeat < RepeatCount; repeat++) + { + var repeatStartTime = StartTime + repeat * repeatDuration; + var reversed = repeat % 2 == 1; + + for (var d = tickDistance; d <= length; d += tickDistance) + { + if (d > length - minDistanceFromEnd) + break; + + var distanceProgress = d / length; + var timeProgress = reversed ? 1 - distanceProgress : distanceProgress; + + yield return new SliderTick + { + RepeatIndex = repeat, + StartTime = repeatStartTime + timeProgress * repeatDuration, + Position = Curve.PositionAt(distanceProgress) - StackedPosition, + StackHeight = StackHeight, + Scale = Scale, + Colour = Colour, + Sample = new HitSampleInfo + { + Type = SampleType.None, + Set = SampleSet.Soft, + }, + }; + } + } + } + } } public enum CurveTypes diff --git a/osu.Game.Modes.Osu/Objects/SliderTick.cs b/osu.Game.Modes.Osu/Objects/SliderTick.cs new file mode 100644 index 0000000000..de8f3f4b6f --- /dev/null +++ b/osu.Game.Modes.Osu/Objects/SliderTick.cs @@ -0,0 +1,9 @@ +using OpenTK; + +namespace osu.Game.Modes.Osu.Objects +{ + public class SliderTick : OsuHitObject + { + public int RepeatIndex { get; set; } + } +} diff --git a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj index e0b9f5c904..19f0df55b8 100644 --- a/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj +++ b/osu.Game.Modes.Osu/osu.Game.Modes.Osu.csproj @@ -47,6 +47,7 @@ + @@ -57,6 +58,7 @@ + @@ -64,6 +66,7 @@ +