Merge branch 'master' into tournament-mappool-flow-logic

This commit is contained in:
Dan Balasescu 2020-03-23 10:33:58 +09:00 committed by GitHub
commit 296a594d0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 306 additions and 159 deletions

View File

@ -97,8 +97,10 @@ platform :ios do
changelog.gsub!('$BUILD_ID', options[:build]) changelog.gsub!('$BUILD_ID', options[:build])
pilot( pilot(
wait_processing_interval: 1800, wait_processing_interval: 900,
changelog: changelog, changelog: changelog,
groups: ['osu! supporters', 'public'],
distribute_external: true,
ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa' ipa: './osu.iOS/bin/iPhone/Release/osu.iOS.ipa'
) )
end end

View File

@ -52,6 +52,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.317.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.319.0" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -4,48 +4,43 @@
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Testing;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Tests.Visual;
using osuTK; using osuTK;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneHitCircleArea : ManualInputManagerTestScene public class TestSceneHitCircleArea : OsuManualInputManagerTestScene
{ {
private HitCircle hitCircle; private HitCircle hitCircle;
private DrawableHitCircle drawableHitCircle; private DrawableHitCircle drawableHitCircle;
private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea; private DrawableHitCircle.HitReceptor hitAreaReceptor => drawableHitCircle.HitArea;
[SetUp] [SetUp]
public new void SetUp() public void SetUp() => Schedule(() =>
{ {
base.SetUp(); hitCircle = new HitCircle
Schedule(() =>
{ {
hitCircle = new HitCircle Position = new Vector2(100, 100),
{ StartTime = Time.Current + 500
Position = new Vector2(100, 100), };
StartTime = Time.Current + 500
};
hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); hitCircle.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty());
Child = new SkinProvidingContainer(new DefaultSkin()) Child = new SkinProvidingContainer(new DefaultSkin())
{
RelativeSizeAxes = Axes.Both,
Child = drawableHitCircle = new DrawableHitCircle(hitCircle)
{ {
RelativeSizeAxes = Axes.Both, Size = new Vector2(100)
Child = drawableHitCircle = new DrawableHitCircle(hitCircle) }
{ };
Size = new Vector2(100) });
}
};
});
}
[Test] [Test]
public void TestCircleHitCentre() public void TestCircleHitCentre()

View File

@ -23,7 +23,7 @@ using osuTK.Graphics;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneOsuDistanceSnapGrid : ManualInputManagerTestScene public class TestSceneOsuDistanceSnapGrid : OsuManualInputManagerTestScene
{ {
private const double beat_length = 100; private const double beat_length = 100;
private static readonly Vector2 grid_position = new Vector2(512, 384); private static readonly Vector2 grid_position = new Vector2(512, 384);

View File

@ -12,7 +12,7 @@ using osu.Game.Tests.Visual;
namespace osu.Game.Rulesets.Osu.Tests namespace osu.Game.Rulesets.Osu.Tests
{ {
public class TestSceneResumeOverlay : ManualInputManagerTestScene public class TestSceneResumeOverlay : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -41,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(DrawableSliderTick), typeof(DrawableSliderTick),
typeof(DrawableSliderTail), typeof(DrawableSliderTail),
typeof(DrawableSliderHead), typeof(DrawableSliderHead),
typeof(DrawableRepeatPoint), typeof(DrawableSliderRepeat),
typeof(DrawableOsuHitObject) typeof(DrawableOsuHitObject)
}; };
@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle)); AddAssert("head samples updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderTick>().All(assertTickSamples)); AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderTick>().All(assertTickSamples));
AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples)); AddAssert("repeat samples updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderRepeat>().All(assertSamples));
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0); AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick"; static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";
@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle)); AddAssert("head samples not updated", () => assertSamples(((Slider)slider.HitObject).HeadCircle));
AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderTick>().All(assertTickSamples)); AddAssert("tick samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderTick>().All(assertTickSamples));
AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<RepeatPoint>().All(assertSamples)); AddAssert("repeat samples not updated", () => ((Slider)slider.HitObject).NestedHitObjects.OfType<SliderRepeat>().All(assertSamples));
AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0); AddAssert("tail has no samples", () => ((Slider)slider.HitObject).TailCircle.Samples.Count == 0);
static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick"; static bool assertTickSamples(SliderTick tick) => tick.Samples.Single().Name == "slidertick";

View File

@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests
typeof(SliderBall), typeof(SliderBall),
typeof(DrawableSlider), typeof(DrawableSlider),
typeof(DrawableSliderTick), typeof(DrawableSliderTick),
typeof(DrawableRepeatPoint), typeof(DrawableSliderRepeat),
typeof(DrawableOsuHitObject), typeof(DrawableOsuHitObject),
typeof(DrawableSliderHead), typeof(DrawableSliderHead),
typeof(DrawableSliderTail), typeof(DrawableSliderTail),
@ -327,7 +327,7 @@ namespace osu.Game.Rulesets.Osu.Tests
AddAssert("Tracking dropped", assertMidSliderJudgementFail); AddAssert("Tracking dropped", assertMidSliderJudgementFail);
} }
private bool assertGreatJudge() => judgementResults.Last().Type == HitResult.Great; private bool assertGreatJudge() => judgementResults.Any() && judgementResults.All(t => t.Type == HitResult.Great);
private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss; private bool assertHeadMissTailTracked() => judgementResults[^2].Type == HitResult.Great && judgementResults.First().Type == HitResult.Miss;

View File

@ -0,0 +1,16 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Judgements
{
public class OsuIgnoreJudgement : OsuJudgement
{
public override bool AffectsCombo => false;
protected override int NumericResultFor(HitResult result) => 0;
protected override double HealthIncreaseFor(HitResult result) => 0;
}
}

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods
return; return;
slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType<SliderTick>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
slider.NestedHitObjects.OfType<RepeatPoint>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y)); slider.NestedHitObjects.OfType<SliderRepeat>().ForEach(h => h.Position = new Vector2(h.Position.X, OsuPlayfield.BASE_SIZE.Y - h.Position.Y));
foreach (var point in slider.Path.ControlPoints) foreach (var point in slider.Path.ControlPoints)
point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y); point.Position.Value = new Vector2(point.Position.Value.X, -point.Position.Value.Y);

View File

@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Osu.Mods
case DrawableSliderHead _: case DrawableSliderHead _:
case DrawableSliderTail _: case DrawableSliderTail _:
case DrawableSliderTick _: case DrawableSliderTick _:
case DrawableRepeatPoint _: case DrawableSliderRepeat _:
return; return;
default: default:

View File

@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Mods
// Wiggle the repeat points with the slider instead of independently. // Wiggle the repeat points with the slider instead of independently.
// Also fixes an issue with repeat points being positioned incorrectly. // Also fixes an issue with repeat points being positioned incorrectly.
if (osuObject is RepeatPoint) if (osuObject is SliderRepeat)
return; return;
Random objRand = new Random((int)osuObject.StartTime); Random objRand = new Random((int)osuObject.StartTime);

View File

@ -88,8 +88,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
private void refresh() private void refresh()
{ {
ClearInternal();
OsuHitObject osuStart = Start.HitObject; OsuHitObject osuStart = Start.HitObject;
double startTime = osuStart.GetEndTime(); double startTime = osuStart.GetEndTime();
@ -116,6 +114,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
double? firstTransformStartTime = null; double? firstTransformStartTime = null;
double finalTransformEndTime = startTime; double finalTransformEndTime = startTime;
int point = 0;
for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing) for (int d = (int)(spacing * 1.5); d < distance - spacing; d += spacing)
{ {
float fraction = (float)d / distance; float fraction = (float)d / distance;
@ -126,13 +126,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
FollowPoint fp; FollowPoint fp;
AddInternal(fp = new FollowPoint if (InternalChildren.Count > point)
{ {
Position = pointStartPosition, fp = (FollowPoint)InternalChildren[point];
Rotation = rotation, fp.ClearTransforms();
Alpha = 0, }
Scale = new Vector2(1.5f * osuEnd.Scale), else
}); AddInternal(fp = new FollowPoint());
fp.Position = pointStartPosition;
fp.Rotation = rotation;
fp.Alpha = 0;
fp.Scale = new Vector2(1.5f * osuEnd.Scale);
if (firstTransformStartTime == null) if (firstTransformStartTime == null)
firstTransformStartTime = fadeInTime; firstTransformStartTime = fadeInTime;
@ -146,8 +151,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn; finalTransformEndTime = fadeOutTime + osuEnd.TimeFadeIn;
} }
point++;
} }
int excessPoints = InternalChildren.Count - point;
for (int i = 0; i < excessPoints; i++)
RemoveInternal(InternalChildren[^1]);
// todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed. // todo: use Expire() on FollowPoints and take lifetime from them when https://github.com/ppy/osu-framework/issues/3300 is fixed.
LifetimeStart = firstTransformStartTime ?? startTime; LifetimeStart = firstTransformStartTime ?? startTime;
LifetimeEnd = finalTransformEndTime; LifetimeEnd = finalTransformEndTime;

View File

@ -6,13 +6,11 @@ using osuTK;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -26,12 +24,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public readonly SliderBall Ball; public readonly SliderBall Ball;
public readonly SkinnableDrawable Body; public readonly SkinnableDrawable Body;
public override bool DisplayResult => false;
private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody; private PlaySliderBody sliderBody => Body.Drawable as PlaySliderBody;
private readonly Container<DrawableSliderHead> headContainer; private readonly Container<DrawableSliderHead> headContainer;
private readonly Container<DrawableSliderTail> tailContainer; private readonly Container<DrawableSliderTail> tailContainer;
private readonly Container<DrawableSliderTick> tickContainer; private readonly Container<DrawableSliderTick> tickContainer;
private readonly Container<DrawableRepeatPoint> repeatContainer; private readonly Container<DrawableSliderRepeat> repeatContainer;
private readonly Slider slider; private readonly Slider slider;
@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling), Body = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SliderBody), _ => new DefaultSliderBody(), confineMode: ConfineMode.NoScaling),
tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both }, tickContainer = new Container<DrawableSliderTick> { RelativeSizeAxes = Axes.Both },
repeatContainer = new Container<DrawableRepeatPoint> { RelativeSizeAxes = Axes.Both }, repeatContainer = new Container<DrawableSliderRepeat> { RelativeSizeAxes = Axes.Both },
Ball = new SliderBall(s, this) Ball = new SliderBall(s, this)
{ {
GetInitialHitAction = () => HeadCircle.HitAction, GetInitialHitAction = () => HeadCircle.HitAction,
@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
tickContainer.Add(tick); tickContainer.Add(tick);
break; break;
case DrawableRepeatPoint repeat: case DrawableSliderRepeat repeat:
repeatContainer.Add(repeat); repeatContainer.Add(repeat);
break; break;
} }
@ -129,8 +129,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
case SliderTick tick: case SliderTick tick:
return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position }; return new DrawableSliderTick(tick) { Position = tick.Position - slider.Position };
case RepeatPoint repeat: case SliderRepeat repeat:
return new DrawableRepeatPoint(repeat, this) { Position = repeat.Position - slider.Position }; return new DrawableSliderRepeat(repeat, this) { Position = repeat.Position - slider.Position };
} }
return base.CreateNestedHitObject(hitObject); return base.CreateNestedHitObject(hitObject);
@ -193,22 +193,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (userTriggered || Time.Current < slider.EndTime) if (userTriggered || Time.Current < slider.EndTime)
return; return;
ApplyResult(r => ApplyResult(r => r.Type = r.Judgement.MaxResult);
{
var judgementsCount = NestedHitObjects.Count;
var judgementsHit = NestedHitObjects.Count(h => h.IsHit);
var hitFraction = (double)judgementsHit / judgementsCount;
if (hitFraction == 1 && HeadCircle.Result.Type == HitResult.Great)
r.Type = HitResult.Great;
else if (hitFraction >= 0.5 && HeadCircle.Result.Type >= HitResult.Good)
r.Type = HitResult.Good;
else if (hitFraction > 0)
r.Type = HitResult.Meh;
else
r.Type = HitResult.Miss;
});
} }
protected override void UpdateStateTransforms(ArmedState state) protected override void UpdateStateTransforms(ArmedState state)

View File

@ -14,19 +14,19 @@ using osuTK;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
public class DrawableRepeatPoint : DrawableOsuHitObject, ITrackSnaking public class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking
{ {
private readonly RepeatPoint repeatPoint; private readonly SliderRepeat sliderRepeat;
private readonly DrawableSlider drawableSlider; private readonly DrawableSlider drawableSlider;
private double animDuration; private double animDuration;
private readonly Drawable scaleContainer; private readonly Drawable scaleContainer;
public DrawableRepeatPoint(RepeatPoint repeatPoint, DrawableSlider drawableSlider) public DrawableSliderRepeat(SliderRepeat sliderRepeat, DrawableSlider drawableSlider)
: base(repeatPoint) : base(sliderRepeat)
{ {
this.repeatPoint = repeatPoint; this.sliderRepeat = sliderRepeat;
this.drawableSlider = drawableSlider; this.drawableSlider = drawableSlider;
Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2);
@ -48,13 +48,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (repeatPoint.StartTime <= Time.Current) if (sliderRepeat.StartTime <= Time.Current)
ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = drawableSlider.Tracking.Value ? HitResult.Great : HitResult.Miss);
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()
{ {
animDuration = Math.Min(300, repeatPoint.SpanDuration); animDuration = Math.Min(300, sliderRepeat.SpanDuration);
this.Animate( this.Animate(
d => d.FadeIn(animDuration), d => d.FadeIn(animDuration),
@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
public void UpdateSnakingPosition(Vector2 start, Vector2 end) public void UpdateSnakingPosition(Vector2 start, Vector2 end)
{ {
bool isRepeatAtEnd = repeatPoint.RepeatIndex % 2 == 0; bool isRepeatAtEnd = sliderRepeat.RepeatIndex % 2 == 0;
List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve; List<Vector2> curve = ((PlaySliderBody)drawableSlider.Body.Drawable).CurrentCurve;
Position = isRepeatAtEnd ? end : start; Position = isRepeatAtEnd ? end : start;

View File

@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (!userTriggered && timeOffset >= 0) if (!userTriggered && timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss);
} }
private void updatePosition() => Position = HitObject.Position - slider.Position; private void updatePosition() => Position = HitObject.Position - slider.Position;

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (timeOffset >= 0) if (timeOffset >= 0)
ApplyResult(r => r.Type = Tracking ? HitResult.Great : HitResult.Miss); ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : HitResult.Miss);
} }
protected override void UpdateInitialTransforms() protected override void UpdateInitialTransforms()

View File

@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects
break; break;
case SliderEventType.Repeat: case SliderEventType.Repeat:
AddNested(new RepeatPoint AddNested(new SliderRepeat
{ {
RepeatIndex = e.SpanIndex, RepeatIndex = e.SpanIndex,
SpanDuration = SpanDuration, SpanDuration = SpanDuration,
@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Objects
foreach (var tick in NestedHitObjects.OfType<SliderTick>()) foreach (var tick in NestedHitObjects.OfType<SliderTick>())
tick.Samples = sampleList; tick.Samples = sampleList;
foreach (var repeat in NestedHitObjects.OfType<RepeatPoint>()) foreach (var repeat in NestedHitObjects.OfType<SliderRepeat>())
repeat.Samples = getNodeSamples(repeat.RepeatIndex + 1); repeat.Samples = getNodeSamples(repeat.RepeatIndex + 1);
if (HeadCircle != null) if (HeadCircle != null)
@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Objects
private IList<HitSampleInfo> getNodeSamples(int nodeIndex) => private IList<HitSampleInfo> getNodeSamples(int nodeIndex) =>
nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples; nodeIndex < NodeSamples.Count ? NodeSamples[nodeIndex] : Samples;
public override Judgement CreateJudgement() => new OsuJudgement(); public override Judgement CreateJudgement() => new OsuIgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
} }

View File

@ -10,7 +10,7 @@ using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Osu.Objects namespace osu.Game.Rulesets.Osu.Objects
{ {
public class RepeatPoint : OsuHitObject public class SliderRepeat : OsuHitObject
{ {
public int RepeatIndex { get; set; } public int RepeatIndex { get; set; }
public double SpanDuration { get; set; } public double SpanDuration { get; set; }
@ -28,8 +28,15 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = Math.Min(SpanDuration * 2, TimePreempt); TimePreempt = Math.Min(SpanDuration * 2, TimePreempt);
} }
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderRepeatJudgement();
public class SliderRepeatJudgement : OsuJudgement
{
public override bool IsBonus => true;
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 30 : 0;
}
} }
} }

View File

@ -22,8 +22,8 @@ namespace osu.Game.Rulesets.Osu.Objects
pathVersion.BindValueChanged(_ => Position = slider.EndPosition); pathVersion.BindValueChanged(_ => Position = slider.EndPosition);
} }
public override Judgement CreateJudgement() => new IgnoreJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderRepeat.SliderRepeatJudgement();
} }
} }

View File

@ -30,8 +30,15 @@ namespace osu.Game.Rulesets.Osu.Objects
TimePreempt = (StartTime - SpanStartTime) / 2 + offset; TimePreempt = (StartTime - SpanStartTime) / 2 + offset;
} }
public override Judgement CreateJudgement() => new OsuJudgement();
protected override HitWindows CreateHitWindows() => HitWindows.Empty; protected override HitWindows CreateHitWindows() => HitWindows.Empty;
public override Judgement CreateJudgement() => new SliderTickJudgement();
public class SliderTickJudgement : OsuJudgement
{
public override bool IsBonus => true;
protected override int NumericResultFor(HitResult result) => result == MaxResult ? 10 : 0;
}
} }
} }

View File

@ -37,7 +37,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Background namespace osu.Game.Tests.Visual.Background
{ {
[TestFixture] [TestFixture]
public class TestSceneUserDimBackgrounds : ManualInputManagerTestScene public class TestSceneUserDimBackgrounds : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Components namespace osu.Game.Tests.Visual.Components
{ {
[TestFixture] [TestFixture]
public class TestSceneIdleTracker : ManualInputManagerTestScene public class TestSceneIdleTracker : OsuManualInputManagerTestScene
{ {
private IdleTrackingBox box1; private IdleTrackingBox box1;
private IdleTrackingBox box2; private IdleTrackingBox box2;

View File

@ -3,27 +3,83 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Allocation; using System.Linq;
using NUnit.Framework;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Testing;
using osu.Game.Screens.Edit; using osu.Game.Screens.Edit;
using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Screens.Edit.Compose.Components;
using osuTK; using osuTK;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
public class TestSceneBeatDivisorControl : OsuTestScene public class TestSceneBeatDivisorControl : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(BindableBeatDivisor) };
private BeatDivisorControl beatDivisorControl;
private BindableBeatDivisor bindableBeatDivisor;
[BackgroundDependencyLoader] private SliderBar<int> tickSliderBar;
private void load() private EquilateralTriangle tickMarkerHead;
[SetUp]
public void SetUp() => Schedule(() =>
{ {
Child = new BeatDivisorControl(new BindableBeatDivisor()) Child = beatDivisorControl = new BeatDivisorControl(bindableBeatDivisor = new BindableBeatDivisor(16))
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Size = new Vector2(90, 90) Size = new Vector2(90, 90)
}; };
tickSliderBar = beatDivisorControl.ChildrenOfType<SliderBar<int>>().Single();
tickMarkerHead = tickSliderBar.ChildrenOfType<EquilateralTriangle>().Single();
});
[Test]
public void TestBindableBeatDivisor()
{
AddRepeatStep("move previous", () => bindableBeatDivisor.Previous(), 4);
AddAssert("divisor is 4", () => bindableBeatDivisor.Value == 4);
AddRepeatStep("move next", () => bindableBeatDivisor.Next(), 3);
AddAssert("divisor is 12", () => bindableBeatDivisor.Value == 12);
}
[Test]
public void TestMouseInput()
{
AddStep("hold marker", () =>
{
InputManager.MoveMouseTo(tickMarkerHead.ScreenSpaceDrawQuad.Centre);
InputManager.PressButton(MouseButton.Left);
});
AddStep("move to 8 and release", () =>
{
InputManager.MoveMouseTo(tickSliderBar.ScreenSpaceDrawQuad.Centre);
InputManager.ReleaseButton(MouseButton.Left);
});
AddAssert("divisor is 8", () => bindableBeatDivisor.Value == 8);
AddStep("hold marker", () => InputManager.PressButton(MouseButton.Left));
AddStep("move to 16", () => InputManager.MoveMouseTo(getPositionForDivisor(16)));
AddStep("move to ~10 and release", () =>
{
InputManager.MoveMouseTo(getPositionForDivisor(10));
InputManager.ReleaseButton(MouseButton.Left);
});
AddAssert("divisor clamped to 8", () => bindableBeatDivisor.Value == 8);
}
private Vector2 getPositionForDivisor(int divisor)
{
var relativePosition = (float)Math.Clamp(divisor, 0, 16) / 16;
var sliderDrawQuad = tickSliderBar.ScreenSpaceDrawQuad;
return new Vector2(
sliderDrawQuad.TopLeft.X + sliderDrawQuad.Width * relativePosition,
sliderDrawQuad.Centre.Y
);
} }
} }
} }

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.Editor namespace osu.Game.Tests.Visual.Editor
{ {
public class TestSceneZoomableScrollContainer : ManualInputManagerTestScene public class TestSceneZoomableScrollContainer : OsuManualInputManagerTestScene
{ {
private ZoomableScrollContainer scrollContainer; private ZoomableScrollContainer scrollContainer;
private Drawable innerBox; private Drawable innerBox;

View File

@ -18,7 +18,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[Description("player pause/fail screens")] [Description("player pause/fail screens")]
public class TestSceneGameplayMenuOverlay : ManualInputManagerTestScene public class TestSceneGameplayMenuOverlay : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) }; public override IReadOnlyList<Type> RequiredTypes => new[] { typeof(FailOverlay), typeof(PauseOverlay) };

View File

@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestSceneHUDOverlay : ManualInputManagerTestScene public class TestSceneHUDOverlay : OsuManualInputManagerTestScene
{ {
private HUDOverlay hudOverlay; private HUDOverlay hudOverlay;

View File

@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[Description("'Hold to Quit' UI element")] [Description("'Hold to Quit' UI element")]
public class TestSceneHoldForMenuButton : ManualInputManagerTestScene public class TestSceneHoldForMenuButton : OsuManualInputManagerTestScene
{ {
private bool exitAction; private bool exitAction;

View File

@ -13,7 +13,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[TestFixture] [TestFixture]
public class TestSceneKeyCounter : ManualInputManagerTestScene public class TestSceneKeyCounter : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -29,7 +29,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
public class TestScenePlayerLoader : ManualInputManagerTestScene public class TestScenePlayerLoader : OsuManualInputManagerTestScene
{ {
private TestPlayerLoader loader; private TestPlayerLoader loader;
private TestPlayerLoaderContainer container; private TestPlayerLoaderContainer container;

View File

@ -14,7 +14,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
[TestFixture] [TestFixture]
public class TestSceneSkipOverlay : ManualInputManagerTestScene public class TestSceneSkipOverlay : OsuManualInputManagerTestScene
{ {
private SkipOverlay skip; private SkipOverlay skip;
private int requestCount; private int requestCount;

View File

@ -20,7 +20,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Multiplayer namespace osu.Game.Tests.Visual.Multiplayer
{ {
public class TestSceneDrawableRoomPlaylist : ManualInputManagerTestScene public class TestSceneDrawableRoomPlaylist : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -27,7 +27,7 @@ namespace osu.Game.Tests.Visual.Navigation
/// <summary> /// <summary>
/// A scene which tests full game flow. /// A scene which tests full game flow.
/// </summary> /// </summary>
public abstract class OsuGameTestScene : ManualInputManagerTestScene public abstract class OsuGameTestScene : OsuManualInputManagerTestScene
{ {
private GameHost host; private GameHost host;

View File

@ -22,7 +22,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
public class TestSceneChatOverlay : ManualInputManagerTestScene public class TestSceneChatOverlay : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -32,6 +32,16 @@ namespace osu.Game.Tests.Visual.Ranking
typeof(SmoothCircularProgress) typeof(SmoothCircularProgress)
}; };
[Test]
public void TestLowDRank()
{
var score = createScore();
score.Accuracy = 0.2;
score.Rank = ScoreRank.D;
addCircleStep(score);
}
[Test] [Test]
public void TestDRank() public void TestDRank()
{ {

View File

@ -15,7 +15,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneCommentEditor : ManualInputManagerTestScene public class TestSceneCommentEditor : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -17,7 +17,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
[TestFixture] [TestFixture]
public class TestSceneCursors : ManualInputManagerTestScene public class TestSceneCursors : OsuManualInputManagerTestScene
{ {
private readonly MenuCursorContainer menuCursorContainer; private readonly MenuCursorContainer menuCursorContainer;
private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6]; private readonly CustomCursorBox[] cursorBoxes = new CustomCursorBox[6];

View File

@ -27,7 +27,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneDeleteLocalScore : ManualInputManagerTestScene public class TestSceneDeleteLocalScore : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -12,7 +12,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
[TestFixture] [TestFixture]
public class TestSceneOsuHoverContainer : ManualInputManagerTestScene public class TestSceneOsuHoverContainer : OsuManualInputManagerTestScene
{ {
private OsuHoverTestContainer hoverContainer; private OsuHoverTestContainer hoverContainer;
private Box colourContainer; private Box colourContainer;

View File

@ -12,7 +12,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneStatefulMenuItem : ManualInputManagerTestScene public class TestSceneStatefulMenuItem : OsuManualInputManagerTestScene
{ {
public override IReadOnlyList<Type> RequiredTypes => new[] public override IReadOnlyList<Type> RequiredTypes => new[]
{ {

View File

@ -9,7 +9,7 @@ using osuTK.Input;
namespace osu.Game.Tests.Visual.UserInterface namespace osu.Game.Tests.Visual.UserInterface
{ {
public class TestSceneSwitchButton : ManualInputManagerTestScene public class TestSceneSwitchButton : OsuManualInputManagerTestScene
{ {
private SwitchButton switchButton; private SwitchButton switchButton;

View File

@ -3,6 +3,7 @@
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
@ -47,23 +48,49 @@ namespace osu.Game.Graphics
{ {
case ScoreRank.XH: case ScoreRank.XH:
case ScoreRank.X: case ScoreRank.X:
return Color4Extensions.FromHex(@"ce1c9d"); return Color4Extensions.FromHex(@"de31ae");
case ScoreRank.SH: case ScoreRank.SH:
case ScoreRank.S: case ScoreRank.S:
return Color4Extensions.FromHex(@"00a8b5"); return Color4Extensions.FromHex(@"02b5c3");
case ScoreRank.A: case ScoreRank.A:
return Color4Extensions.FromHex(@"7cce14"); return Color4Extensions.FromHex(@"88da20");
case ScoreRank.B: case ScoreRank.B:
return Color4Extensions.FromHex(@"e3b130"); return Color4Extensions.FromHex(@"e3b130");
case ScoreRank.C: case ScoreRank.C:
return Color4Extensions.FromHex(@"f18252"); return Color4Extensions.FromHex(@"ff8e5d");
default: default:
return Color4Extensions.FromHex(@"e95353"); return Color4Extensions.FromHex(@"ff5a5a");
}
}
/// <summary>
/// Retrieves the colour for a <see cref="HitResult"/>.
/// </summary>
public Color4 ForHitResult(HitResult judgement)
{
switch (judgement)
{
case HitResult.Perfect:
case HitResult.Great:
return Blue;
case HitResult.Ok:
case HitResult.Good:
return Green;
case HitResult.Meh:
return Yellow;
case HitResult.Miss:
return Red;
default:
return Color4.White;
} }
} }

View File

@ -12,7 +12,6 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Drawables;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
using osuTK.Graphics;
namespace osu.Game.Rulesets.Judgements namespace osu.Game.Rulesets.Judgements
{ {
@ -68,7 +67,7 @@ namespace osu.Game.Rulesets.Judgements
{ {
Text = Result.Type.GetDescription().ToUpperInvariant(), Text = Result.Type.GetDescription().ToUpperInvariant(),
Font = OsuFont.Numeric.With(size: 20), Font = OsuFont.Numeric.With(size: 20),
Colour = judgementColour(Result.Type), Colour = colours.ForHitResult(Result.Type),
Scale = new Vector2(0.85f, 1), Scale = new Vector2(0.85f, 1),
}, confineMode: ConfineMode.NoScaling) }, confineMode: ConfineMode.NoScaling)
}; };
@ -110,28 +109,5 @@ namespace osu.Game.Rulesets.Judgements
Expire(true); Expire(true);
} }
private Color4 judgementColour(HitResult judgement)
{
switch (judgement)
{
case HitResult.Perfect:
case HitResult.Great:
return colours.Blue;
case HitResult.Ok:
case HitResult.Good:
return colours.Green;
case HitResult.Meh:
return colours.Yellow;
case HitResult.Miss:
return colours.Red;
default:
return Color4.White;
}
}
} }
} }

View File

@ -279,6 +279,11 @@ namespace osu.Game.Screens.Edit.Compose.Components
handleMouseInput(e.ScreenSpaceMousePosition); handleMouseInput(e.ScreenSpaceMousePosition);
} }
protected override void OnDragEnd(DragEndEvent e)
{
handleMouseInput(e.ScreenSpaceMousePosition);
}
private void handleMouseInput(Vector2 screenSpaceMousePosition) private void handleMouseInput(Vector2 screenSpaceMousePosition)
{ {
// copied from SliderBar so we can do custom spacing logic. // copied from SliderBar so we can do custom spacing logic.

View File

@ -387,6 +387,10 @@ namespace osu.Game.Screens.Play
private void onCompletion() private void onCompletion()
{ {
// screen may be in the exiting transition phase.
if (!this.IsCurrentScreen())
return;
// Only show the completion screen if the player hasn't failed // Only show the completion screen if the player hasn't failed
if (HealthProcessor.HasFailed || completionProgressDelegate != null) if (HealthProcessor.HasFailed || completionProgressDelegate != null)
return; return;
@ -581,7 +585,7 @@ namespace osu.Game.Screens.Play
if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed) if (completionProgressDelegate != null && !completionProgressDelegate.Cancelled && !completionProgressDelegate.Completed)
{ {
// proceed to result screen if beatmap already finished playing // proceed to result screen if beatmap already finished playing
scheduleGotoRanking(); completionProgressDelegate.RunTask();
return true; return true;
} }
@ -622,8 +626,16 @@ namespace osu.Game.Screens.Play
completionProgressDelegate = Schedule(delegate completionProgressDelegate = Schedule(delegate
{ {
var score = CreateScore(); var score = CreateScore();
if (DrawableRuleset.ReplayScore == null) if (DrawableRuleset.ReplayScore == null)
scoreManager.Import(score).ContinueWith(_ => Schedule(() => this.Push(CreateResults(score)))); {
scoreManager.Import(score).ContinueWith(_ => Schedule(() =>
{
// screen may be in the exiting transition phase.
if (this.IsCurrentScreen())
this.Push(CreateResults(score));
}));
}
else else
this.Push(CreateResults(score)); this.Push(CreateResults(score));
}); });

View File

@ -119,42 +119,42 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
new SmoothCircularProgress new SmoothCircularProgress
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#BE0089"), Colour = OsuColour.ForRank(ScoreRank.X),
InnerRadius = RANK_CIRCLE_RADIUS, InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 1 } Current = { Value = 1 }
}, },
new SmoothCircularProgress new SmoothCircularProgress
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#0096A2"), Colour = OsuColour.ForRank(ScoreRank.S),
InnerRadius = RANK_CIRCLE_RADIUS, InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 1 - virtual_ss_percentage } Current = { Value = 1 - virtual_ss_percentage }
}, },
new SmoothCircularProgress new SmoothCircularProgress
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#72C904"), Colour = OsuColour.ForRank(ScoreRank.A),
InnerRadius = RANK_CIRCLE_RADIUS, InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.95f } Current = { Value = 0.95f }
}, },
new SmoothCircularProgress new SmoothCircularProgress
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#D99D03"), Colour = OsuColour.ForRank(ScoreRank.B),
InnerRadius = RANK_CIRCLE_RADIUS, InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.9f } Current = { Value = 0.9f }
}, },
new SmoothCircularProgress new SmoothCircularProgress
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#EA7948"), Colour = OsuColour.ForRank(ScoreRank.C),
InnerRadius = RANK_CIRCLE_RADIUS, InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.8f } Current = { Value = 0.8f }
}, },
new SmoothCircularProgress new SmoothCircularProgress
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#FF5858"), Colour = OsuColour.ForRank(ScoreRank.D),
InnerRadius = RANK_CIRCLE_RADIUS, InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.7f } Current = { Value = 0.7f }
}, },
@ -196,6 +196,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
new RankBadge(0.9f, ScoreRank.A), new RankBadge(0.9f, ScoreRank.A),
new RankBadge(0.8f, ScoreRank.B), new RankBadge(0.8f, ScoreRank.B),
new RankBadge(0.7f, ScoreRank.C), new RankBadge(0.7f, ScoreRank.C),
new RankBadge(0.35f, ScoreRank.D),
} }
}, },
rankText = new RankText(score.Rank) rankText = new RankText(score.Rank)

View File

@ -5,7 +5,6 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation; using osu.Framework.Localisation;
@ -14,6 +13,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring; using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking.Expanded.Accuracy; using osu.Game.Screens.Ranking.Expanded.Accuracy;
@ -56,13 +56,13 @@ namespace osu.Game.Screens.Ranking.Expanded
var topStatistics = new List<StatisticDisplay> var topStatistics = new List<StatisticDisplay>
{ {
new AccuracyStatistic(score.Accuracy), new AccuracyStatistic(score.Accuracy),
new ComboStatistic(score.MaxCombo, true), new ComboStatistic(score.MaxCombo, !score.Statistics.TryGetValue(HitResult.Miss, out var missCount) || missCount == 0),
new CounterStatistic("pp", (int)(score.PP ?? 0)), new CounterStatistic("pp", (int)(score.PP ?? 0)),
}; };
var bottomStatistics = new List<StatisticDisplay>(); var bottomStatistics = new List<StatisticDisplay>();
foreach (var stat in score.SortedStatistics) foreach (var stat in score.SortedStatistics)
bottomStatistics.Add(new CounterStatistic(stat.Key.GetDescription(), stat.Value)); bottomStatistics.Add(new HitResultStatistic(stat.Key, stat.Value));
statisticDisplays.AddRange(topStatistics); statisticDisplays.AddRange(topStatistics);
statisticDisplays.AddRange(bottomStatistics); statisticDisplays.AddRange(bottomStatistics);
@ -204,6 +204,13 @@ namespace osu.Game.Screens.Ranking.Expanded
} }
} }
} }
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
Text = $"Played on {score.Date.ToLocalTime():g}"
} }
} }
}; };

View File

@ -0,0 +1,27 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Game.Graphics;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
public class HitResultStatistic : CounterStatistic
{
private readonly HitResult result;
public HitResultStatistic(HitResult result, int count)
: base(result.GetDescription(), count)
{
this.result = result;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
HeaderText.Colour = colours.ForHitResult(result);
}
}
}

View File

@ -6,6 +6,7 @@ using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
@ -16,8 +17,9 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
/// </summary> /// </summary>
public abstract class StatisticDisplay : CompositeDrawable public abstract class StatisticDisplay : CompositeDrawable
{ {
private readonly string header; protected SpriteText HeaderText { get; private set; }
private readonly string header;
private Drawable content; private Drawable content;
/// <summary> /// <summary>
@ -53,7 +55,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#222") Colour = Color4Extensions.FromHex("#222")
}, },
new OsuSpriteText HeaderText = new OsuSpriteText
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence. // Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text. // See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
@ -135,7 +136,7 @@ namespace osu.Game.Screens.Ranking
protected override void Update() protected override void Update()
{ {
base.Update(); base.Update();
content.Height = DrawHeight; content.Height = Math.Max(768, DrawHeight);
} }
} }
} }

View File

@ -14,7 +14,7 @@ using osuTK.Graphics;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
public abstract class ManualInputManagerTestScene : OsuTestScene public abstract class OsuManualInputManagerTestScene : OsuTestScene
{ {
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
private readonly Container content; private readonly Container content;
@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual
private readonly TriangleButton buttonTest; private readonly TriangleButton buttonTest;
private readonly TriangleButton buttonLocal; private readonly TriangleButton buttonLocal;
protected ManualInputManagerTestScene() protected OsuManualInputManagerTestScene()
{ {
base.Content.AddRange(new Drawable[] base.Content.AddRange(new Drawable[]
{ {

View File

@ -11,7 +11,7 @@ namespace osu.Game.Tests.Visual
/// <summary> /// <summary>
/// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions). /// A test case which can be used to test a screen (that relies on OnEntering being called to execute startup instructions).
/// </summary> /// </summary>
public abstract class ScreenTestScene : ManualInputManagerTestScene public abstract class ScreenTestScene : OsuManualInputManagerTestScene
{ {
protected readonly OsuScreenStack Stack; protected readonly OsuScreenStack Stack;

View File

@ -8,7 +8,7 @@ using osu.Game.Rulesets.Edit;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
public abstract class SelectionBlueprintTestScene : ManualInputManagerTestScene public abstract class SelectionBlueprintTestScene : OsuManualInputManagerTestScene
{ {
protected override Container<Drawable> Content => content ?? base.Content; protected override Container<Drawable> Content => content ?? base.Content;
private readonly Container content; private readonly Container content;

View File

@ -68,7 +68,7 @@ namespace osu.Game.Users.Drawables
if (!OpenOnClick.Value) if (!OpenOnClick.Value)
return; return;
if (user != null) if (user?.Id > 1)
game?.ShowUser(user.Id); game?.ShowUser(user.Id);
} }

View File

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.317.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.319.0" />
<PackageReference Include="Sentry" Version="2.1.0" /> <PackageReference Include="Sentry" Version="2.1.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -71,7 +71,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.317.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2020.319.0" />
</ItemGroup> </ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. --> <!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies"> <ItemGroup Label="Transitive Dependencies">
@ -79,7 +79,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.317.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.319.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" /> <PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />