Merge branch 'master' into non-concurrent-sample-playback

This commit is contained in:
smoogipoo
2021-02-12 17:22:15 +09:00
295 changed files with 7478 additions and 2398 deletions

View File

@ -110,8 +110,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
double startTime = start.GetEndTime();
double duration = end.StartTime - startTime;
// Preempt time can go below 800ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear preempt function (see: OsuHitObject).
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
double preempt = PREEMPT * Math.Min(1, start.TimePreempt / OsuHitObject.PREEMPT_MIN);
fadeOutTime = startTime + fraction * duration;
fadeInTime = fadeOutTime - PREEMPT;
fadeInTime = fadeOutTime - preempt;
}
}
}

View File

@ -123,7 +123,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
return;
}
var result = HitObject.HitWindows.ResultFor(timeOffset);
var result = ResultFor(timeOffset);
if (result == HitResult.None || CheckHittable?.Invoke(this, Time.Current) == false)
{
@ -146,6 +146,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
});
}
/// <summary>
/// Retrieves the <see cref="HitResult"/> for a time offset.
/// </summary>
/// <param name="timeOffset">The time offset.</param>
/// <returns>The hit result, or <see cref="HitResult.None"/> if <paramref name="timeOffset"/> doesn't result in a judgement.</returns>
protected virtual HitResult ResultFor(double timeOffset) => HitObject.HitWindows.ResultFor(timeOffset);
protected override void UpdateInitialTransforms()
{
base.UpdateInitialTransforms();

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (JudgedObject?.HitObject is OsuHitObject osuObject)
{
Position = osuObject.StackedPosition;
Position = osuObject.StackedEndPosition;
Scale = new Vector2(osuObject.Scale);
}
}

View File

@ -15,6 +15,7 @@ using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Osu.Skinning.Default;
using osu.Game.Rulesets.Osu.UI;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
@ -30,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public SliderBall Ball { get; private set; }
public SkinnableDrawable Body { get; private set; }
public override bool DisplayResult => false;
public override bool DisplayResult => !HitObject.OnlyJudgeNestedObjects;
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
@ -249,7 +250,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (userTriggered || Time.Current < HitObject.EndTime)
return;
ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
// If only the nested hitobjects are judged, then the slider's own judgement is ignored for scoring purposes.
// But the slider needs to still be judged with a reasonable hit/miss result for visual purposes (hit/miss transforms, etc).
if (HitObject.OnlyJudgeNestedObjects)
{
ApplyResult(r => r.Type = NestedHitObjects.Any(h => h.Result.IsHit) ? r.Judgement.MaxResult : r.Judgement.MinResult);
return;
}
// Otherwise, if this slider also needs to be judged, apply judgement proportionally to the number of nested hitobjects hit. This is the classic osu!stable scoring.
ApplyResult(r =>
{
int totalTicks = NestedHitObjects.Count;
int hitTicks = NestedHitObjects.Count(h => h.IsHit);
if (hitTicks == totalTicks)
r.Type = HitResult.Great;
else if (hitTicks == 0)
r.Type = HitResult.Miss;
else
{
double hitFraction = (double)hitTicks / totalTicks;
r.Type = hitFraction >= 0.5 ? HitResult.Ok : HitResult.Meh;
}
});
}
public override void PlaySamples()

View File

@ -7,16 +7,27 @@ using JetBrains.Annotations;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects.Drawables
{
public class DrawableSliderHead : DrawableHitCircle
{
public new SliderHeadCircle HitObject => (SliderHeadCircle)base.HitObject;
[CanBeNull]
public Slider Slider => DrawableSlider?.HitObject;
protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject;
public override bool DisplayResult => HitObject?.JudgeAsNormalHitCircle ?? base.DisplayResult;
/// <summary>
/// Makes this <see cref="DrawableSliderHead"/> track the follow circle when the start time is reached.
/// If <c>false</c>, this <see cref="DrawableSliderHead"/> will be pinned to its initial position in the slider.
/// </summary>
public bool TrackFollowCircle = true;
private readonly IBindable<int> pathVersion = new Bindable<int>();
protected override OsuSkinComponents CirclePieceComponent => OsuSkinComponents.SliderHeadHitCircle;
@ -59,12 +70,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.Update();
Debug.Assert(Slider != null);
Debug.Assert(HitObject != null);
double completionProgress = Math.Clamp((Time.Current - Slider.StartTime) / Slider.Duration, 0, 1);
if (TrackFollowCircle)
{
double completionProgress = Math.Clamp((Time.Current - Slider.StartTime) / Slider.Duration, 0, 1);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!IsHit)
Position = Slider.CurvePositionAt(completionProgress);
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
if (!IsHit)
Position = Slider.CurvePositionAt(completionProgress);
}
}
protected override HitResult ResultFor(double timeOffset)
{
Debug.Assert(HitObject != null);
if (HitObject.JudgeAsNormalHitCircle)
return base.ResultFor(timeOffset);
// If not judged as a normal hitcircle, judge as a slider tick instead. This is the classic osu!stable scoring.
var result = base.ResultFor(timeOffset);
return result.IsHit() ? HitResult.LargeTickHit : HitResult.LargeTickMiss;
}
public Action<double> OnShake;

View File

@ -244,7 +244,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.Update();
if (HandleUserInput)
RotationTracker.Tracking = !Result.HasResult && (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
{
bool isValidSpinningTime = Time.Current >= HitObject.StartTime && Time.Current <= HitObject.EndTime;
bool correctButtonPressed = (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false);
RotationTracker.Tracking = !Result.HasResult
&& correctButtonPressed
&& isValidSpinningTime;
}
if (spinningSample != null && spinnerFrequencyModulate)
spinningSample.Frequency.Value = spinning_sample_modulated_base_frequency + Progress;
@ -256,6 +263,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (!SpmCounter.IsPresent && RotationTracker.Tracking)
SpmCounter.FadeIn(HitObject.TimeFadeIn);
SpmCounter.SetRotation(Result.RateAdjustedRotation);
updateBonusScore();