Merge branch 'master' into starrating-fix

This commit is contained in:
Dan Balasescu
2018-02-01 18:44:04 +09:00
committed by GitHub
60 changed files with 1474 additions and 261 deletions

View File

@ -18,14 +18,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public class DrawableSlider : DrawableOsuHitObject, IDrawableHitObjectWithProxiedApproach
{
private readonly Slider slider;
public readonly DrawableHitCircle InitialCircle;
private readonly List<Drawable> components = new List<Drawable>();
private readonly Container<DrawableSliderTick> ticks;
private readonly Container<DrawableRepeatPoint> repeatPoints;
public readonly DrawableHitCircle HeadCircle;
public readonly SliderBody Body;
public readonly SliderBall Ball;
@ -34,6 +29,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
slider = s;
DrawableSliderTail tail;
Container<DrawableSliderTick> ticks;
Container<DrawableRepeatPoint> repeatPoints;
Children = new Drawable[]
{
Body = new SliderBody(s)
@ -51,42 +50,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
AlwaysPresent = true,
Alpha = 0
},
InitialCircle = new DrawableHitCircle(new HitCircle
{
StartTime = s.StartTime,
Position = s.StackedPosition,
IndexInCurrentCombo = s.IndexInCurrentCombo,
Scale = s.Scale,
ComboColour = s.ComboColour,
Samples = s.Samples,
SampleControlPoint = s.SampleControlPoint,
TimePreempt = s.TimePreempt,
TimeFadein = s.TimeFadein,
HitWindow300 = s.HitWindow300,
HitWindow100 = s.HitWindow100,
HitWindow50 = s.HitWindow50
})
HeadCircle = new DrawableHitCircle(s.HeadCircle),
tail = new DrawableSliderTail(s.TailCircle)
};
components.Add(Body);
components.Add(Ball);
AddNested(InitialCircle);
AddNested(HeadCircle);
AddNested(tail);
components.Add(tail);
foreach (var tick in s.NestedHitObjects.OfType<SliderTick>())
{
var spanStartTime = s.StartTime + tick.SpanIndex * s.SpanDuration;
var fadeInTime = spanStartTime + (tick.StartTime - spanStartTime) / 2 - (tick.SpanIndex == 0 ? HitObject.TimeFadein : HitObject.TimeFadein / 2);
var fadeOutTime = spanStartTime + s.SpanDuration;
var drawableTick = new DrawableSliderTick(tick)
{
FadeInTime = fadeInTime,
FadeOutTime = fadeOutTime,
Position = tick.Position,
Position = tick.Position
};
ticks.Add(drawableTick);
components.Add(drawableTick);
AddNested(drawableTick);
}
@ -121,27 +105,25 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
currentSpan = span;
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!InitialCircle.Judgements.Any(j => j.IsHit))
InitialCircle.Position = slider.Curve.PositionAt(progress);
if (!HeadCircle.IsHit)
HeadCircle.Position = slider.Curve.PositionAt(progress);
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, span);
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
foreach (var t in components.OfType<IRequireTracking>()) t.Tracking = Ball.Tracking;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered && Time.Current >= slider.EndTime)
{
var judgementsCount = ticks.Children.Count + repeatPoints.Children.Count + 1;
var judgementsHit = ticks.Children.Count(t => t.Judgements.Any(j => j.IsHit)) + repeatPoints.Children.Count(t => t.Judgements.Any(j => j.IsHit));
if (InitialCircle.Judgements.Any(j => j.IsHit))
judgementsHit++;
var judgementsCount = NestedHitObjects.Count;
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
if (hitFraction == 1 && InitialCircle.Judgements.Any(j => j.Result == HitResult.Great))
if (hitFraction == 1 && HeadCircle.Judgements.Any(j => j.Result == HitResult.Great))
AddJudgement(new OsuJudgement { Result = HitResult.Great });
else if (hitFraction >= 0.5 && InitialCircle.Judgements.Any(j => j.Result >= HitResult.Good))
else if (hitFraction >= 0.5 && HeadCircle.Judgements.Any(j => j.Result >= HitResult.Good))
AddJudgement(new OsuJudgement { Result = HitResult.Good });
else if (hitFraction > 0)
AddJudgement(new OsuJudgement { Result = HitResult.Meh });
@ -173,7 +155,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
}
}
public Drawable ProxiedLayer => InitialCircle.ApproachCircle;
public Drawable ProxiedLayer => HeadCircle.ApproachCircle;
public override Vector2 SelectionPoint => ToScreenSpace(Body.Position);
public override Quad SelectionQuad => Body.PathDrawQuad;

View File

@ -0,0 +1,32 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking
{
/// <summary>
/// The judgement text is provided by the <see cref="DrawableSlider"/>.
/// </summary>
public override bool DisplayJudgement => false;
public bool Tracking { get; set; }
public DrawableSliderTail(HitCircle hitCircle)
: base(hitCircle)
{
AlwaysPresent = true;
RelativeSizeAxes = Axes.Both;
}
protected override void CheckForJudgements(bool userTriggered, double timeOffset)
{
if (!userTriggered && timeOffset >= 0)
AddJudgement(new OsuSliderTailJudgement { Result = Tracking ? HitResult.Great : HitResult.Miss });
}
}
}

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables;
using OpenTK;
@ -12,21 +11,16 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderTick : DrawableOsuHitObject
public class DrawableSliderTick : DrawableOsuHitObject, IRequireTracking
{
private readonly SliderTick sliderTick;
private const double anim_duration = 150;
public double FadeInTime;
public double FadeOutTime;
public bool Tracking;
public bool Tracking { get; set; }
public override bool DisplayJudgement => false;
public DrawableSliderTick(SliderTick sliderTick) : base(sliderTick)
{
this.sliderTick = sliderTick;
Size = new Vector2(16) * sliderTick.Scale;
Masking = true;
@ -56,13 +50,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void UpdatePreemptState()
{
var animIn = Math.Min(150, sliderTick.StartTime - FadeInTime);
this.Animate(
d => d.FadeIn(animIn),
d => d.ScaleTo(0.5f).ScaleTo(1.2f, animIn)
d => d.FadeIn(anim_duration),
d => d.ScaleTo(0.5f).ScaleTo(1.2f, anim_duration / 2)
).Then(
d => d.ScaleTo(1, 150, Easing.Out)
d => d.ScaleTo(1, anim_duration / 2, Easing.Out)
);
}
@ -71,15 +63,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
switch (state)
{
case ArmedState.Idle:
this.Delay(FadeOutTime - sliderTick.StartTime).FadeOut();
this.Delay(HitObject.TimePreempt).FadeOut();
break;
case ArmedState.Miss:
this.FadeOut(160)
.FadeColour(Color4.Red, 80);
this.FadeOut(anim_duration)
.FadeColour(Color4.Red, anim_duration / 2);
break;
case ArmedState.Hit:
this.FadeOut(120, Easing.OutQuint)
.ScaleTo(Scale * 1.5f, 120, Easing.OutQuint);
this.FadeOut(anim_duration, Easing.OutQuint)
.ScaleTo(Scale * 1.5f, anim_duration, Easing.OutQuint);
break;
}
}

View File

@ -0,0 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public interface IRequireTracking
{
/// <summary>
/// Whether the <see cref="DrawableSlider"/> is currently being tracked by the user.
/// </summary>
bool Tracking { get; set; }
}
}

View File

@ -81,6 +81,9 @@ namespace osu.Game.Rulesets.Osu.Objects
public double Velocity;
public double TickDistance;
public HitCircle HeadCircle;
public HitCircle TailCircle;
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
@ -98,10 +101,37 @@ namespace osu.Game.Rulesets.Osu.Objects
{
base.CreateNestedHitObjects();
createSliderEnds();
createTicks();
createRepeatPoints();
}
private void createSliderEnds()
{
HeadCircle = new HitCircle
{
StartTime = StartTime,
Position = StackedPosition,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboColour = ComboColour,
Samples = Samples,
SampleControlPoint = SampleControlPoint
};
TailCircle = new HitCircle
{
StartTime = EndTime,
Position = StackedEndPosition,
IndexInCurrentCombo = IndexInCurrentCombo,
ComboColour = ComboColour,
Samples = Samples,
SampleControlPoint = SampleControlPoint
};
AddNested(HeadCircle);
AddNested(TailCircle);
}
private void createTicks()
{
if (TickDistance == 0) return;
@ -138,6 +168,7 @@ namespace osu.Game.Rulesets.Osu.Objects
AddNested(new SliderTick
{
SpanIndex = span,
SpanStartTime = spanStartTime,
StartTime = spanStartTime + timeProgress * SpanDuration,
Position = Curve.PositionAt(distanceProgress),
StackHeight = StackHeight,

View File

@ -1,10 +1,30 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints;
namespace osu.Game.Rulesets.Osu.Objects
{
public class SliderTick : OsuHitObject
{
public int SpanIndex { get; set; }
public double SpanStartTime { get; set; }
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
{
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
double offset;
if (SpanIndex > 0)
// Adding 200 to include the offset stable used.
// This is so on repeats ticks don't appear too late to be visually processed by the player.
offset = 200;
else
offset = TimeFadein * 0.66f;
TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
}
}
}