1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-13 14:12:56 +08:00

Merge remote-tracking branch 'refs/remotes/ppy/master' into news

This commit is contained in:
Andrei Zavatski 2020-07-16 14:38:50 +03:00
commit 17c598568d
83 changed files with 1593 additions and 286 deletions

View File

@ -51,7 +51,7 @@
<Reference Include="Java.Interop" /> <Reference Include="Java.Interop" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.622.1" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.715.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.710.0" /> <PackageReference Include="ppy.osu.Framework.Android" Version="2020.714.1" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -9,7 +9,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" /> <PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
<PackageReference Include="nunit" Version="3.12.0" /> <PackageReference Include="nunit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@ -0,0 +1,56 @@
// 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 System.Collections.Generic;
using System.Linq;
using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Testing;
using osu.Game.Beatmaps;
using osu.Game.Configuration;
using osu.Game.Rulesets.Catch.Mods;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Catch.Objects.Drawables;
using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
using osu.Game.Tests.Visual;
using osuTK;
namespace osu.Game.Rulesets.Catch.Tests
{
public class TestSceneCatchModHidden : ModTestScene
{
[BackgroundDependencyLoader]
private void load()
{
LocalConfig.Set(OsuSetting.IncreaseFirstObjectVisibility, false);
}
[Test]
public void TestJuiceStream()
{
CreateModTest(new ModTestData
{
Beatmap = new Beatmap
{
HitObjects = new List<HitObject>
{
new JuiceStream
{
StartTime = 1000,
Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(0, -192) }),
X = CatchPlayfield.WIDTH / 2
}
}
},
Mod = new CatchModHidden(),
PassCondition = () => Player.Results.Count > 0
&& Player.ChildrenOfType<DrawableJuiceStream>().Single().Alpha > 0
&& Player.ChildrenOfType<DrawableFruit>().Last().Alpha > 0
});
}
protected override Ruleset CreatePlayerRuleset() => new CatchRuleset();
}
}

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -1,23 +1,16 @@
// 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.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mods;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModFadeIn : Mod public class ManiaModFadeIn : ManiaModHidden
{ {
public override string Name => "Fade In"; public override string Name => "Fade In";
public override string Acronym => "FI"; public override string Acronym => "FI";
public override IconUsage? Icon => OsuIcon.ModHidden; public override IconUsage? Icon => OsuIcon.ModHidden;
public override ModType Type => ModType.DifficultyIncrease;
public override string Description => @"Keys appear out of nowhere!"; public override string Description => @"Keys appear out of nowhere!";
public override double ScoreMultiplier => 1;
public override bool Ranked => true;
public override Type[] IncompatibleMods => new[] { typeof(ModFlashlight<ManiaHitObject>) };
} }
} }

View File

@ -7,6 +7,10 @@ namespace osu.Game.Rulesets.Mania.Scoring
{ {
internal class ManiaScoreProcessor : ScoreProcessor internal class ManiaScoreProcessor : ScoreProcessor
{ {
protected override double DefaultAccuracyPortion => 0.8;
protected override double DefaultComboPortion => 0.2;
public override HitWindows CreateHitWindows() => new ManiaHitWindows(); public override HitWindows CreateHitWindows() => new ManiaHitWindows();
} }
} }

View File

@ -2,9 +2,12 @@
// 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 System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Pooling;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables;
@ -16,14 +19,46 @@ namespace osu.Game.Rulesets.Osu.Tests
{ {
public TestSceneDrawableJudgement() public TestSceneDrawableJudgement()
{ {
var pools = new List<DrawablePool<DrawableOsuJudgement>>();
foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1)) foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Skip(1))
{ {
AddStep("Show " + result.GetDescription(), () => SetContents(() => AddStep("Show " + result.GetDescription(), () =>
new DrawableOsuJudgement(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null) {
int poolIndex = 0;
SetContents(() =>
{ {
Anchor = Anchor.Centre, DrawablePool<DrawableOsuJudgement> pool;
Origin = Anchor.Centre,
})); if (poolIndex >= pools.Count)
pools.Add(pool = new DrawablePool<DrawableOsuJudgement>(1));
else
{
pool = pools[poolIndex];
// We need to make sure neither the pool nor the judgement get disposed when new content is set, and they both share the same parent.
((Container)pool.Parent).Clear(false);
}
var container = new Container
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
pool,
pool.Get(j => j.Apply(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null)).With(j =>
{
j.Anchor = Anchor.Centre;
j.Origin = Anchor.Centre;
})
}
};
poolIndex++;
return container;
});
});
} }
} }
} }

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public BindableNumber<float> CircleSize { get; } = new BindableFloat public BindableNumber<float> CircleSize { get; } = new BindableFloat
{ {
Precision = 0.1f, Precision = 0.1f,
MinValue = 1, MinValue = 0,
MaxValue = 10, MaxValue = 10,
Default = 5, Default = 5,
Value = 5, Value = 5,
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Mods
public BindableNumber<float> ApproachRate { get; } = new BindableFloat public BindableNumber<float> ApproachRate { get; } = new BindableFloat
{ {
Precision = 0.1f, Precision = 0.1f,
MinValue = 1, MinValue = 0,
MaxValue = 10, MaxValue = 10,
Default = 5, Default = 5,
Value = 5, Value = 5,

View File

@ -62,6 +62,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
if (lighting != null) if (lighting != null)
{ {
lighting.ResetAnimation();
if (JudgedObject != null) if (JudgedObject != null)
{ {
lightingColour = JudgedObject.AccentColour.GetBoundCopy(); lightingColour = JudgedObject.AccentColour.GetBoundCopy();

View File

@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Taiko.Tests
[TestCase("basic")] [TestCase("basic")]
[TestCase("slider-generating-drumroll")] [TestCase("slider-generating-drumroll")]
[TestCase("sample-to-type-conversions")] [TestCase("sample-to-type-conversions")]
[TestCase("slider-conversion-v6")]
[TestCase("slider-conversion-v14")]
public void Test(string name) => base.Test(name); public void Test(string name) => base.Test(name);
protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject) protected override IEnumerable<ConvertValue> CreateConvertValue(HitObject hitObject)

View File

@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -10,6 +10,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Game.Audio; using osu.Game.Audio;
using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.ControlPoints;
using osu.Game.Beatmaps.Formats;
namespace osu.Game.Rulesets.Taiko.Beatmaps namespace osu.Game.Rulesets.Taiko.Beatmaps
{ {
@ -82,37 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
{ {
case IHasDistance distanceData: case IHasDistance distanceData:
{ {
// Number of spans of the object - one for the initial length and for each repeat if (shouldConvertSliderToHits(obj, beatmap, distanceData, out var taikoDuration, out var tickSpacing))
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
double speedAdjustment = difficultyPoint.SpeedMultiplier;
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER;
// The velocity of the taiko hit object - calculated as the velocity of a drum roll
double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
// The duration of the taiko hit object
double taikoDuration = distance / taikoVelocity;
// The velocity of the osu! hit object - calculated as the velocity of a slider
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
// The duration of the osu! hit object
double osuDuration = distance / osuVelocity;
// osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
// only uses it for tick rate if beatmap version < 8
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
speedAdjustedBeatLength *= speedAdjustment;
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
{ {
List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples }); List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
@ -184,6 +155,52 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
} }
} }
private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out double taikoDuration, out double tickSpacing)
{
// DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS.
// Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable.
// Rounding cannot be used as an alternative since the error deltas have been observed to be between 1e-2 and 1e-6.
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER;
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
double beatLength;
#pragma warning disable 618
if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint)
#pragma warning restore 618
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
else
beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier;
double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate;
// The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll.
double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate;
taikoDuration = distance / taikoVelocity * beatLength;
if (isForCurrentRuleset)
{
tickSpacing = 0;
return false;
}
double osuVelocity = taikoVelocity * (1000f / beatLength);
// osu-stable always uses the speed-adjusted beatlength to determine the osu! velocity, but only uses it for conversion if beatmap version < 8
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
beatLength = timingPoint.BeatLength;
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
return tickSpacing > 0
&& distance / osuVelocity * 1000 < 2 * beatLength;
}
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap(); protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
} }
} }

View File

@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty.Skills
double addition = 1; double addition = 1;
// We get an extra addition if we are not a slider or spinner // We get an extra addition if we are not a slider or spinner
if (current.LastObject is Hit && current.BaseObject is Hit && current.DeltaTime < 1000) if (current.LastObject is Hit && current.BaseObject is Hit && current.BaseObject.StartTime - current.LastObject.StartTime < 1000)
{ {
if (hasColourChange(current)) if (hasColourChange(current))
addition += 0.75; addition += 0.75;

View File

@ -0,0 +1,379 @@
{
"Mappings": [{
"StartTime": 2000,
"Objects": [{
"StartTime": 2000,
"EndTime": 2000,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
},
{
"StartTime": 2173,
"EndTime": 2173,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
},
{
"StartTime": 4000,
"Objects": [{
"StartTime": 4000,
"EndTime": 4000,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 4173,
"EndTime": 4173,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
},
{
"StartTime": 6000,
"Objects": [{
"StartTime": 6000,
"EndTime": 6000,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 6271,
"EndTime": 6271,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 6542,
"EndTime": 6542,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
},
{
"StartTime": 8000,
"Objects": [{
"StartTime": 8000,
"EndTime": 8000,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8026,
"EndTime": 8026,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8053,
"EndTime": 8053,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8080,
"EndTime": 8080,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8107,
"EndTime": 8107,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8133,
"EndTime": 8133,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8160,
"EndTime": 8160,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8187,
"EndTime": 8187,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8214,
"EndTime": 8214,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8241,
"EndTime": 8241,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8267,
"EndTime": 8267,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8294,
"EndTime": 8294,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8321,
"EndTime": 8321,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8348,
"EndTime": 8348,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8374,
"EndTime": 8374,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8401,
"EndTime": 8401,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8428,
"EndTime": 8428,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8455,
"EndTime": 8455,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8482,
"EndTime": 8482,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8508,
"EndTime": 8508,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8535,
"EndTime": 8535,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8562,
"EndTime": 8562,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8589,
"EndTime": 8589,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8615,
"EndTime": 8615,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8642,
"EndTime": 8642,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8669,
"EndTime": 8669,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8696,
"EndTime": 8696,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8723,
"EndTime": 8723,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8749,
"EndTime": 8749,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8776,
"EndTime": 8776,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8803,
"EndTime": 8803,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8830,
"EndTime": 8830,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 8857,
"EndTime": 8857,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
}
]
}

View File

@ -0,0 +1,32 @@
osu file format v14
[General]
Mode: 0
[Difficulty]
HPDrainRate:7
CircleSize:4
OverallDifficulty:8
ApproachRate:9.2
SliderMultiplier:2.3
SliderTickRate:1
[TimingPoints]
0,333.333333333333,4,1,0,50,1,0
2000,-100,4,2,0,80,0,0
6000,389.61038961039,4,2,1,60,1,0
8000,428.571428571429,4,3,1,65,1,0
8000,-133.333333333333,4,1,1,45,0,0
[HitObjects]
// Should convert.
48,32,2000,6,0,B|168:32,1,120,4|2
312,68,4000,2,0,B|288:52|256:44|216:52|200:68,1,120,0|8
// Should convert.
184,224,6000,2,0,L|336:308,2,160,2|2|0,0:0|0:0|0:0,0:0:0:0:
// Should convert.
328,36,8000,6,0,L|332:16,32,10.7812504112721,0|0,0:0,0:0:0:0:

View File

@ -0,0 +1,137 @@
{
"Mappings": [{
"StartTime": 0,
"Objects": [{
"StartTime": 0,
"EndTime": 0,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
},
{
"StartTime": 162,
"EndTime": 162,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 325,
"EndTime": 325,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
},
{
"StartTime": 487,
"EndTime": 487,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 650,
"EndTime": 650,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
},
{
"StartTime": 813,
"EndTime": 813,
"IsRim": false,
"IsCentre": true,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 975,
"EndTime": 975,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
}
]
},
{
"StartTime": 2000,
"Objects": [{
"StartTime": 2000,
"EndTime": 2000,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 2162,
"EndTime": 2162,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 2325,
"EndTime": 2325,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
},
{
"StartTime": 2487,
"EndTime": 2487,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 2650,
"EndTime": 2650,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
},
{
"StartTime": 2813,
"EndTime": 2813,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": true
},
{
"StartTime": 2975,
"EndTime": 2975,
"IsRim": true,
"IsCentre": false,
"IsDrumRoll": false,
"IsSwell": false,
"IsStrong": false
}
]
}
]
}

View File

@ -0,0 +1,20 @@
osu file format v6
[General]
Mode: 0
[Difficulty]
HPDrainRate:3
CircleSize:4
OverallDifficulty:1
SliderMultiplier:1.2
SliderTickRate:3
[TimingPoints]
0,487.884208814441,4,1,0,60,1,0
2000,-100,4,1,0,65,0,1
[HitObjects]
// Should convert.
376,64,0,6,0,B|256:32|136:64,1,240,6|0
256,120,2000,6,8,C|264:192|336:192,2,120,8|8|6

View File

@ -7,6 +7,10 @@ namespace osu.Game.Rulesets.Taiko.Scoring
{ {
internal class TaikoScoreProcessor : ScoreProcessor internal class TaikoScoreProcessor : ScoreProcessor
{ {
protected override double DefaultAccuracyPortion => 0.75;
protected override double DefaultComboPortion => 0.25;
public override HitWindows CreateHitWindows() => new TaikoHitWindows(); public override HitWindows CreateHitWindows() => new TaikoHitWindows();
} }
} }

View File

@ -8,7 +8,6 @@ using osu.Game.Online.API.Requests;
using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Tests.Visual; using osu.Game.Tests.Visual;
using osu.Game.Users;
namespace osu.Game.Tests.Online namespace osu.Game.Tests.Online
{ {
@ -55,7 +54,7 @@ namespace osu.Game.Tests.Online
AddStep("fire request", () => AddStep("fire request", () =>
{ {
gotResponse = false; gotResponse = false;
request = new LeaveChannelRequest(new Channel(), new User()); request = new LeaveChannelRequest(new Channel());
request.Success += () => gotResponse = true; request.Success += () => gotResponse = true;
API.Queue(request); API.Queue(request);
}); });
@ -74,7 +73,7 @@ namespace osu.Game.Tests.Online
AddStep("fire request", () => AddStep("fire request", () =>
{ {
gotResponse = false; gotResponse = false;
request = new LeaveChannelRequest(new Channel(), new User()); request = new LeaveChannelRequest(new Channel());
request.Success += () => gotResponse = true; request.Success += () => gotResponse = true;
API.Perform(request); API.Perform(request);
}); });
@ -93,7 +92,7 @@ namespace osu.Game.Tests.Online
AddStep("fire request", () => AddStep("fire request", () =>
{ {
gotResponse = false; gotResponse = false;
request = new LeaveChannelRequest(new Channel(), new User()); request = new LeaveChannelRequest(new Channel());
request.Success += () => gotResponse = true; request.Success += () => gotResponse = true;
API.PerformAsync(request); API.PerformAsync(request);
}); });

View File

@ -11,6 +11,7 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Screens.Play; using osu.Game.Screens.Play;
using osu.Game.Screens.Play.Break; using osu.Game.Screens.Play.Break;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osuTK.Input;
namespace osu.Game.Tests.Visual.Gameplay namespace osu.Game.Tests.Visual.Gameplay
{ {
@ -35,6 +36,18 @@ namespace osu.Game.Tests.Visual.Gameplay
AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000)); AddStep("rewind", () => Player.GameplayClockContainer.Seek(-80000));
AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0)); AddUntilStep("key counter reset", () => Player.HUDOverlay.KeyCounter.Children.All(kc => kc.CountPresses == 0));
double? time = null;
AddStep("store time", () => time = Player.GameplayClockContainer.GameplayClock.CurrentTime);
// test seek via keyboard
AddStep("seek with right arrow key", () => press(Key.Right));
AddAssert("time seeked forward", () => Player.GameplayClockContainer.GameplayClock.CurrentTime > time + 2000);
AddStep("store time", () => time = Player.GameplayClockContainer.GameplayClock.CurrentTime);
AddStep("seek with left arrow key", () => press(Key.Left));
AddAssert("time seeked backward", () => Player.GameplayClockContainer.GameplayClock.CurrentTime < time);
seekToBreak(0); seekToBreak(0);
seekToBreak(1); seekToBreak(1);
@ -54,5 +67,11 @@ namespace osu.Game.Tests.Visual.Gameplay
BreakPeriod destBreak() => Beatmap.Value.Beatmap.Breaks.ElementAt(breakIndex); BreakPeriod destBreak() => Beatmap.Value.Beatmap.Breaks.ElementAt(breakIndex);
} }
private void press(Key key)
{
InputManager.PressKey(key);
InputManager.ReleaseKey(key);
}
} }
} }

View File

@ -15,6 +15,7 @@ using osu.Game.Rulesets.Catch;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Rankings;
namespace osu.Game.Tests.Visual.Online namespace osu.Game.Tests.Visual.Online
{ {
@ -105,7 +106,7 @@ namespace osu.Game.Tests.Visual.Online
{ {
onLoadStarted(); onLoadStarted();
request = new GetSpotlightRankingsRequest(ruleset, spotlight); request = new GetSpotlightRankingsRequest(ruleset, spotlight, RankingsSortCriteria.All);
((GetSpotlightRankingsRequest)request).Success += rankings => Schedule(() => ((GetSpotlightRankingsRequest)request).Success += rankings => Schedule(() =>
{ {
var table = new ScoresTable(1, rankings.Users); var table = new ScoresTable(1, rankings.Users);

View File

@ -80,9 +80,9 @@ namespace osu.Game.Tests.Visual.SongSelect
[Test] [Test]
public void TestRecommendedSelection() public void TestRecommendedSelection()
{ {
loadBeatmaps(); loadBeatmaps(carouselAdjust: carousel => carousel.GetRecommendedBeatmap = beatmaps => beatmaps.LastOrDefault());
AddStep("set recommendation function", () => carousel.GetRecommendedBeatmap = beatmaps => beatmaps.LastOrDefault()); AddStep("select last", () => carousel.SelectBeatmap(carousel.BeatmapSets.Last().Beatmaps.Last()));
// check recommended was selected // check recommended was selected
advanceSelection(direction: 1, diff: false); advanceSelection(direction: 1, diff: false);
@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.SongSelect
{ {
loadBeatmaps(); loadBeatmaps();
advanceSelection(direction: 1, diff: false); AddStep("select first", () => carousel.SelectBeatmap(carousel.BeatmapSets.First().Beatmaps.First()));
waitForSelection(1, 1); waitForSelection(1, 1);
advanceSelection(direction: 1, diff: true); advanceSelection(direction: 1, diff: true);
@ -707,9 +707,9 @@ namespace osu.Game.Tests.Visual.SongSelect
checkVisibleItemCount(true, 15); checkVisibleItemCount(true, 15);
} }
private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null) private void loadBeatmaps(List<BeatmapSetInfo> beatmapSets = null, Func<FilterCriteria> initialCriteria = null, Action<BeatmapCarousel> carouselAdjust = null)
{ {
createCarousel(); createCarousel(carouselAdjust);
if (beatmapSets == null) if (beatmapSets == null)
{ {
@ -730,17 +730,21 @@ namespace osu.Game.Tests.Visual.SongSelect
AddUntilStep("Wait for load", () => changed); AddUntilStep("Wait for load", () => changed);
} }
private void createCarousel(Container target = null) private void createCarousel(Action<BeatmapCarousel> carouselAdjust = null, Container target = null)
{ {
AddStep("Create carousel", () => AddStep("Create carousel", () =>
{ {
selectedSets.Clear(); selectedSets.Clear();
eagerSelectedIDs.Clear(); eagerSelectedIDs.Clear();
(target ?? this).Child = carousel = new TestBeatmapCarousel carousel = new TestBeatmapCarousel
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}; };
carouselAdjust?.Invoke(carousel);
(target ?? this).Child = carousel;
}); });
} }

View File

@ -0,0 +1,67 @@
// 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.Overlays.Comments.Buttons;
using osu.Framework.Graphics;
using osu.Framework.Allocation;
using osu.Game.Overlays;
using osu.Framework.Graphics.Containers;
using osuTK;
using NUnit.Framework;
using System.Linq;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Testing;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneCommentRepliesButton : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Blue);
private readonly TestButton button;
public TestSceneCommentRepliesButton()
{
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 10),
Children = new Drawable[]
{
button = new TestButton(),
new LoadRepliesButton
{
Action = () => { }
},
new ShowRepliesButton(1),
new ShowRepliesButton(2)
}
};
}
[Test]
public void TestArrowDirection()
{
AddStep("Set upwards", () => button.SetIconDirection(true));
AddAssert("Icon facing upwards", () => button.Icon.Scale.Y == -1);
AddStep("Set downwards", () => button.SetIconDirection(false));
AddAssert("Icon facing downwards", () => button.Icon.Scale.Y == 1);
}
private class TestButton : CommentRepliesButton
{
public SpriteIcon Icon => this.ChildrenOfType<SpriteIcon>().First();
public TestButton()
{
Text = "sample text";
}
public new void SetIconDirection(bool upwards) => base.SetIconDirection(upwards);
}
}
}

View File

@ -0,0 +1,46 @@
// 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 NUnit.Framework;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Tests.Visual.UserInterface
{
[TestFixture]
public class TestSceneHueAnimation : OsuTestScene
{
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
HueAnimation anim2;
Add(anim2 = new HueAnimation
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Intro/Triangles/logo-highlight"),
Colour = Colour4.White,
});
HueAnimation anim;
Add(anim = new HueAnimation
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
Texture = textures.Get("Intro/Triangles/logo-background"),
Colour = OsuColour.Gray(0.6f),
});
AddSliderStep("Progress", 0f, 1f, 0f, newValue =>
{
anim2.AnimationProgress = newValue;
anim.AnimationProgress = newValue;
});
}
}
}

View File

@ -13,6 +13,8 @@ using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Mods; using osu.Game.Overlays.Mods;
using osu.Game.Rulesets; using osu.Game.Rulesets;
using osu.Game.Rulesets.Mania;
using osu.Game.Rulesets.Mania.Mods;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Mods;
@ -99,6 +101,12 @@ namespace osu.Game.Tests.Visual.UserInterface
public void TestManiaMods() public void TestManiaMods()
{ {
changeRuleset(3); changeRuleset(3);
var mania = new ManiaRuleset();
testModsWithSameBaseType(
mania.GetAllMods().Single(m => m.GetType() == typeof(ManiaModFadeIn)),
mania.GetAllMods().Single(m => m.GetType() == typeof(ManiaModHidden)));
} }
[Test] [Test]
@ -197,6 +205,18 @@ namespace osu.Game.Tests.Visual.UserInterface
checkLabelColor(() => Color4.White); checkLabelColor(() => Color4.White);
} }
private void testModsWithSameBaseType(Mod modA, Mod modB)
{
selectNext(modA);
checkSelected(modA);
selectNext(modB);
checkSelected(modB);
// Backwards
selectPrevious(modA);
checkSelected(modA);
}
private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1)); private void selectNext(Mod mod) => AddStep($"left click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(1));
private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1)); private void selectPrevious(Mod mod) => AddStep($"right click {mod.Name}", () => modSelect.GetModButton(mod)?.SelectNext(-1));

View File

@ -0,0 +1,25 @@
// 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.Graphics;
using osu.Game.Overlays;
using osu.Game.Overlays.Rankings;
namespace osu.Game.Tests.Visual.UserInterface
{
public class TestSceneRankingsSortTabControl : OsuTestScene
{
[Cached]
private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green);
public TestSceneRankingsSortTabControl()
{
Child = new RankingsSortTabControl
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre
};
}
}
}

View File

@ -5,7 +5,7 @@
<PackageReference Include="DeepEqual" Version="2.0.0" /> <PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
<PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Update="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">

View File

@ -7,7 +7,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" /> <PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.15.1" /> <PackageReference Include="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup> </ItemGroup>
<PropertyGroup Label="Project"> <PropertyGroup Label="Project">
<OutputType>WinExe</OutputType> <OutputType>WinExe</OutputType>

View File

@ -57,7 +57,7 @@ namespace osu.Game.Beatmaps
beatmap.BeatmapInfo = original.BeatmapInfo; beatmap.BeatmapInfo = original.BeatmapInfo;
beatmap.ControlPointInfo = original.ControlPointInfo; beatmap.ControlPointInfo = original.ControlPointInfo;
beatmap.HitObjects = convertHitObjects(original.HitObjects, original); beatmap.HitObjects = convertHitObjects(original.HitObjects, original).OrderBy(s => s.StartTime).ToList();
beatmap.Breaks = original.Breaks; beatmap.Breaks = original.Breaks;
return beatmap; return beatmap;

View File

@ -48,16 +48,13 @@ namespace osu.Game.Beatmaps
public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken) public Task UpdateAsync(BeatmapSetInfo beatmapSet, CancellationToken cancellationToken)
{ {
if (api?.State != APIState.Online)
return Task.CompletedTask;
LogForModel(beatmapSet, "Performing online lookups..."); LogForModel(beatmapSet, "Performing online lookups...");
return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray()); return Task.WhenAll(beatmapSet.Beatmaps.Select(b => UpdateAsync(beatmapSet, b, cancellationToken)).ToArray());
} }
// todo: expose this when we need to do individual difficulty lookups. // todo: expose this when we need to do individual difficulty lookups.
protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken) protected Task UpdateAsync(BeatmapSetInfo beatmapSet, BeatmapInfo beatmap, CancellationToken cancellationToken)
=> Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler, updateScheduler); => Task.Factory.StartNew(() => lookup(beatmapSet, beatmap), cancellationToken, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler);
private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap) private void lookup(BeatmapSetInfo set, BeatmapInfo beatmap)
{ {

View File

@ -369,7 +369,9 @@ namespace osu.Game.Beatmaps.Formats
addControlPoint(time, controlPoint, true); addControlPoint(time, controlPoint, true);
} }
addControlPoint(time, new LegacyDifficultyControlPoint #pragma warning disable 618
addControlPoint(time, new LegacyDifficultyControlPoint(beatLength)
#pragma warning restore 618
{ {
SpeedMultiplier = speedMultiplier, SpeedMultiplier = speedMultiplier,
}, timingChange); }, timingChange);

View File

@ -159,11 +159,20 @@ namespace osu.Game.Beatmaps.Formats
Mania, Mania,
} }
internal class LegacyDifficultyControlPoint : DifficultyControlPoint [Obsolete("Do not use unless you're a legacy ruleset and 100% sure.")]
public class LegacyDifficultyControlPoint : DifficultyControlPoint
{ {
public LegacyDifficultyControlPoint() /// <summary>
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
/// DO NOT USE THIS UNLESS 100% SURE.
/// </summary>
public readonly float BpmMultiplier;
public LegacyDifficultyControlPoint(double beatLength)
{ {
SpeedMultiplierBindable.Precision = double.Epsilon; SpeedMultiplierBindable.Precision = double.Epsilon;
BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1;
} }
} }

View File

@ -91,6 +91,7 @@ namespace osu.Game.Configuration
Set(OsuSetting.FadePlayfieldWhenHealthLow, true); Set(OsuSetting.FadePlayfieldWhenHealthLow, true);
Set(OsuSetting.KeyOverlay, false); Set(OsuSetting.KeyOverlay, false);
Set(OsuSetting.PositionalHitSounds, true); Set(OsuSetting.PositionalHitSounds, true);
Set(OsuSetting.AlwaysPlayFirstComboBreak, true);
Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth); Set(OsuSetting.ScoreMeter, ScoreMeterType.HitErrorBoth);
Set(OsuSetting.FloatingComments, false); Set(OsuSetting.FloatingComments, false);
@ -180,6 +181,7 @@ namespace osu.Game.Configuration
ShowStoryboard, ShowStoryboard,
KeyOverlay, KeyOverlay,
PositionalHitSounds, PositionalHitSounds,
AlwaysPlayFirstComboBreak,
ScoreMeter, ScoreMeter,
FloatingComments, FloatingComments,
ShowInterface, ShowInterface,

View File

@ -0,0 +1,69 @@
// 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 System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.OpenGL.Vertices;
using osu.Framework.Graphics.Shaders;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
namespace osu.Game.Graphics.Sprites
{
public class HueAnimation : Sprite
{
[BackgroundDependencyLoader]
private void load(ShaderManager shaders, TextureStore textures)
{
TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"HueAnimation");
RoundedTextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, @"HueAnimation"); // Masking isn't supported for now
}
private float animationProgress;
public float AnimationProgress
{
get => animationProgress;
set
{
if (animationProgress == value) return;
animationProgress = value;
Invalidate(Invalidation.DrawInfo);
}
}
public override bool IsPresent => true;
protected override DrawNode CreateDrawNode() => new HueAnimationDrawNode(this);
private class HueAnimationDrawNode : SpriteDrawNode
{
private HueAnimation source => (HueAnimation)Source;
private float progress;
public HueAnimationDrawNode(HueAnimation source)
: base(source)
{
}
public override void ApplyState()
{
base.ApplyState();
progress = source.animationProgress;
}
protected override void Blit(Action<TexturedVertex2D> vertexAction)
{
Shader.GetUniform<float>("progress").UpdateValue(ref progress);
base.Blit(vertexAction);
}
protected override bool CanDrawOpaqueInterior => false;
}
}
}

View File

@ -35,6 +35,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar), new KeyBinding(new[] { InputKey.Control, InputKey.T }, GlobalAction.ToggleToolbar),
new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings),
new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect),
new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications),
new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.Escape, GlobalAction.Back),
new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back),
@ -56,6 +57,7 @@ namespace osu.Game.Input.Bindings
new KeyBinding(new[] { InputKey.Control, InputKey.Tilde }, GlobalAction.QuickExit), new KeyBinding(new[] { InputKey.Control, InputKey.Tilde }, GlobalAction.QuickExit),
new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed), new KeyBinding(new[] { InputKey.Control, InputKey.Plus }, GlobalAction.IncreaseScrollSpeed),
new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed), new KeyBinding(new[] { InputKey.Control, InputKey.Minus }, GlobalAction.DecreaseScrollSpeed),
new KeyBinding(InputKey.MouseMiddle, GlobalAction.PauseGameplay),
}; };
public IEnumerable<KeyBinding> AudioControlKeyBindings => new[] public IEnumerable<KeyBinding> AudioControlKeyBindings => new[]
@ -157,5 +159,11 @@ namespace osu.Game.Input.Bindings
[Description("Home")] [Description("Home")]
Home, Home,
[Description("Toggle notifications")]
ToggleNotifications,
[Description("Pause")]
PauseGameplay,
} }
} }

View File

@ -5,6 +5,7 @@ using System;
using Newtonsoft.Json; using Newtonsoft.Json;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Framework.Logging; using osu.Framework.Logging;
using osu.Game.Users;
namespace osu.Game.Online.API namespace osu.Game.Online.API
{ {
@ -61,6 +62,11 @@ namespace osu.Game.Online.API
protected APIAccess API; protected APIAccess API;
protected WebRequest WebRequest; protected WebRequest WebRequest;
/// <summary>
/// The currently logged in user. Note that this will only be populated during <see cref="Perform"/>.
/// </summary>
protected User User { get; private set; }
/// <summary> /// <summary>
/// Invoked on successful completion of an API request. /// Invoked on successful completion of an API request.
/// This will be scheduled to the API's internal scheduler (run on update thread automatically). /// This will be scheduled to the API's internal scheduler (run on update thread automatically).
@ -86,6 +92,7 @@ namespace osu.Game.Online.API
} }
API = apiAccess; API = apiAccess;
User = apiAccess.LocalUser.Value;
if (checkAndScheduleFailure()) if (checkAndScheduleFailure())
return; return;

View File

@ -2,6 +2,7 @@
// 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 osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Overlays.Rankings;
using osu.Game.Rulesets; using osu.Game.Rulesets;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
@ -9,11 +10,13 @@ namespace osu.Game.Online.API.Requests
public class GetSpotlightRankingsRequest : GetRankingsRequest<GetSpotlightRankingsResponse> public class GetSpotlightRankingsRequest : GetRankingsRequest<GetSpotlightRankingsResponse>
{ {
private readonly int spotlight; private readonly int spotlight;
private readonly RankingsSortCriteria sort;
public GetSpotlightRankingsRequest(RulesetInfo ruleset, int spotlight) public GetSpotlightRankingsRequest(RulesetInfo ruleset, int spotlight, RankingsSortCriteria sort)
: base(ruleset, 1) : base(ruleset, 1)
{ {
this.spotlight = spotlight; this.spotlight = spotlight;
this.sort = sort;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -21,6 +24,7 @@ namespace osu.Game.Online.API.Requests
var req = base.CreateWebRequest(); var req = base.CreateWebRequest();
req.AddParameter("spotlight", spotlight.ToString()); req.AddParameter("spotlight", spotlight.ToString());
req.AddParameter("filter", sort.ToString().ToLower());
return req; return req;
} }

View File

@ -4,19 +4,16 @@
using System.Net.Http; using System.Net.Http;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class JoinChannelRequest : APIRequest public class JoinChannelRequest : APIRequest
{ {
private readonly Channel channel; private readonly Channel channel;
private readonly User user;
public JoinChannelRequest(Channel channel, User user) public JoinChannelRequest(Channel channel)
{ {
this.channel = channel; this.channel = channel;
this.user = user;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -26,6 +23,6 @@ namespace osu.Game.Online.API.Requests
return req; return req;
} }
protected override string Target => $@"chat/channels/{channel.Id}/users/{user.Id}"; protected override string Target => $@"chat/channels/{channel.Id}/users/{User.Id}";
} }
} }

View File

@ -4,19 +4,16 @@
using System.Net.Http; using System.Net.Http;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class JoinRoomRequest : APIRequest public class JoinRoomRequest : APIRequest
{ {
private readonly Room room; private readonly Room room;
private readonly User user;
public JoinRoomRequest(Room room, User user) public JoinRoomRequest(Room room)
{ {
this.room = room; this.room = room;
this.user = user;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -26,6 +23,6 @@ namespace osu.Game.Online.API.Requests
return req; return req;
} }
protected override string Target => $"rooms/{room.RoomID.Value}/users/{user.Id}"; protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}";
} }
} }

View File

@ -4,19 +4,16 @@
using System.Net.Http; using System.Net.Http;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class LeaveChannelRequest : APIRequest public class LeaveChannelRequest : APIRequest
{ {
private readonly Channel channel; private readonly Channel channel;
private readonly User user;
public LeaveChannelRequest(Channel channel, User user) public LeaveChannelRequest(Channel channel)
{ {
this.channel = channel; this.channel = channel;
this.user = user;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -26,6 +23,6 @@ namespace osu.Game.Online.API.Requests
return req; return req;
} }
protected override string Target => $@"chat/channels/{channel.Id}/users/{user.Id}"; protected override string Target => $@"chat/channels/{channel.Id}/users/{User.Id}";
} }
} }

View File

@ -4,19 +4,16 @@
using System.Net.Http; using System.Net.Http;
using osu.Framework.IO.Network; using osu.Framework.IO.Network;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Users;
namespace osu.Game.Online.API.Requests namespace osu.Game.Online.API.Requests
{ {
public class PartRoomRequest : APIRequest public class PartRoomRequest : APIRequest
{ {
private readonly Room room; private readonly Room room;
private readonly User user;
public PartRoomRequest(Room room, User user) public PartRoomRequest(Room room)
{ {
this.room = room; this.room = room;
this.user = user;
} }
protected override WebRequest CreateWebRequest() protected override WebRequest CreateWebRequest()
@ -26,6 +23,6 @@ namespace osu.Game.Online.API.Requests
return req; return req;
} }
protected override string Target => $"rooms/{room.RoomID.Value}/users/{user.Id}"; protected override string Target => $"rooms/{room.RoomID.Value}/users/{User.Id}";
} }
} }

View File

@ -381,7 +381,7 @@ namespace osu.Game.Online.Chat
break; break;
default: default:
var req = new JoinChannelRequest(channel, api.LocalUser.Value); var req = new JoinChannelRequest(channel);
req.Success += () => joinChannel(channel, fetchInitialMessages); req.Success += () => joinChannel(channel, fetchInitialMessages);
req.Failure += ex => LeaveChannel(channel); req.Failure += ex => LeaveChannel(channel);
api.Queue(req); api.Queue(req);
@ -410,7 +410,7 @@ namespace osu.Game.Online.Chat
if (channel.Joined.Value) if (channel.Joined.Value)
{ {
api.Queue(new LeaveChannelRequest(channel, api.LocalUser.Value)); api.Queue(new LeaveChannelRequest(channel));
channel.Joined.Value = false; channel.Joined.Value = false;
} }
} }

View File

@ -170,36 +170,37 @@ namespace osu.Game.Online.Leaderboards
{ {
InternalChildren = new Drawable[] InternalChildren = new Drawable[]
{ {
new GridContainer new OsuContextMenuContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RowDimensions = new[] Masking = true,
Child = new GridContainer
{ {
new Dimension(), RelativeSizeAxes = Axes.Both,
new Dimension(GridSizeMode.AutoSize), RowDimensions = new[]
},
Content = new[]
{
new Drawable[]
{ {
new OsuContextMenuContainer new Dimension(),
new Dimension(GridSizeMode.AutoSize),
},
Content = new[]
{
new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, scrollContainer = new OsuScrollContainer
Child = scrollContainer = new OsuScrollContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
ScrollbarVisible = false, ScrollbarVisible = false,
} }
},
new Drawable[]
{
content = new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
},
} }
}, },
new Drawable[]
{
content = new Container
{
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
},
}
}, },
}, },
loading = new LoadingSpinner(), loading = new LoadingSpinner(),

View File

@ -58,6 +58,9 @@ namespace osu.Game.Online.Leaderboards
[Resolved(CanBeNull = true)] [Resolved(CanBeNull = true)]
private DialogOverlay dialogOverlay { get; set; } private DialogOverlay dialogOverlay { get; set; }
[Resolved(CanBeNull = true)]
private SongSelect songSelect { get; set; }
public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true) public LeaderboardScore(ScoreInfo score, int? rank, bool allowHighlight = true)
{ {
this.score = score; this.score = score;
@ -373,6 +376,9 @@ namespace osu.Game.Online.Leaderboards
{ {
List<MenuItem> items = new List<MenuItem>(); List<MenuItem> items = new List<MenuItem>();
if (score.Mods.Length > 0 && modsContainer.Any(s => s.IsHovered) && songSelect != null)
items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = score.Mods));
if (score.ID != 0) if (score.ID != 0)
items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score)))); items.Add(new OsuMenuItem("Delete", MenuItemType.Destructive, () => dialogOverlay?.Push(new LocalScoreDeleteDialog(score))));

View File

@ -573,7 +573,9 @@ namespace osu.Game
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Action = () => Action = () =>
{ {
if ((ScreenStack.CurrentScreen as IOsuScreen)?.AllowBackButton == true) var currentScreen = ScreenStack.CurrentScreen as IOsuScreen;
if (currentScreen?.AllowBackButton == true && !currentScreen.OnBackButton())
ScreenStack.Exit(); ScreenStack.Exit();
} }
}, },
@ -890,6 +892,10 @@ namespace osu.Game
beatmapListing.ToggleVisibility(); beatmapListing.ToggleVisibility();
return true; return true;
case GlobalAction.ToggleNotifications:
notifications.ToggleVisibility();
return true;
case GlobalAction.ToggleGameplayMouseButtons: case GlobalAction.ToggleGameplayMouseButtons:
LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get<bool>(OsuSetting.MouseDisableButtons)); LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get<bool>(OsuSetting.MouseDisableButtons));
return true; return true;

View File

@ -152,6 +152,7 @@ namespace osu.Game
AddFont(Resources, @"Fonts/Noto-Hangul"); AddFont(Resources, @"Fonts/Noto-Hangul");
AddFont(Resources, @"Fonts/Noto-CJK-Basic"); AddFont(Resources, @"Fonts/Noto-CJK-Basic");
AddFont(Resources, @"Fonts/Noto-CJK-Compatibility"); AddFont(Resources, @"Fonts/Noto-CJK-Compatibility");
AddFont(Resources, @"Fonts/Noto-Thai");
AddFont(Resources, @"Fonts/Venera-Light"); AddFont(Resources, @"Fonts/Venera-Light");
AddFont(Resources, @"Fonts/Venera-Bold"); AddFont(Resources, @"Fonts/Venera-Bold");

View File

@ -10,6 +10,7 @@ using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.UserInterface; using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -114,21 +115,26 @@ namespace osu.Game.Overlays.Chat
Colour = Color4.Black.Opacity(0.3f), Colour = Color4.Black.Opacity(0.3f),
Type = EdgeEffectType.Shadow, Type = EdgeEffectType.Shadow,
}, },
// Drop shadow effect
Child = new Container Child = new Container
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Y = 3,
Masking = true, Masking = true,
CornerRadius = 4, CornerRadius = 4,
EdgeEffect = new EdgeEffectParameters Children = new Drawable[]
{ {
Radius = 1, new Box
Colour = Color4Extensions.FromHex(message.Sender.Colour), {
Type = EdgeEffectType.Shadow, RelativeSizeAxes = Axes.Both,
}, Colour = Color4Extensions.FromHex(message.Sender.Colour),
Padding = new MarginPadding { Left = 3, Right = 3, Bottom = 1, Top = -3 }, },
Y = 3, new Container
Child = username, {
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Left = 4, Right = 4, Bottom = 1, Top = -2 },
Child = username
}
}
} }
}; };
} }

View File

@ -0,0 +1,117 @@
// 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.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Input.Events;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Overlays.Comments.Buttons
{
public abstract class CommentRepliesButton : CompositeDrawable
{
protected string Text
{
get => text.Text;
set => text.Text = value;
}
[Resolved]
private OverlayColourProvider colourProvider { get; set; }
private readonly SpriteIcon icon;
private readonly Box background;
private readonly OsuSpriteText text;
protected CommentRepliesButton()
{
AutoSizeAxes = Axes.Both;
Margin = new MarginPadding
{
Vertical = 2
};
InternalChildren = new Drawable[]
{
new CircularContainer
{
AutoSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
background = new Box
{
RelativeSizeAxes = Axes.Both
},
new Container
{
AutoSizeAxes = Axes.Both,
Margin = new MarginPadding
{
Vertical = 5,
Horizontal = 10,
},
Child = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
text = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AlwaysPresent = true,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)
},
icon = new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(7.5f),
Icon = FontAwesome.Solid.ChevronDown
}
}
}
}
}
},
new HoverClickSounds(),
};
}
[BackgroundDependencyLoader]
private void load()
{
background.Colour = colourProvider.Background2;
icon.Colour = colourProvider.Foreground1;
}
protected void SetIconDirection(bool upwards) => icon.ScaleTo(new Vector2(1, upwards ? -1 : 1));
public void ToggleTextVisibility(bool visible) => text.FadeTo(visible ? 1 : 0, 200, Easing.OutQuint);
protected override bool OnHover(HoverEvent e)
{
base.OnHover(e);
background.FadeColour(colourProvider.Background1, 200, Easing.OutQuint);
icon.FadeColour(colourProvider.Light1, 200, Easing.OutQuint);
return true;
}
protected override void OnHoverLost(HoverLostEvent e)
{
base.OnHoverLost(e);
background.FadeColour(colourProvider.Background2, 200, Easing.OutQuint);
icon.FadeColour(colourProvider.Foreground1, 200, Easing.OutQuint);
}
}
}

View File

@ -0,0 +1,32 @@
// 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.Graphics;
using osu.Game.Graphics.UserInterface;
namespace osu.Game.Overlays.Comments.Buttons
{
public class LoadRepliesButton : LoadingButton
{
private ButtonContent content;
public LoadRepliesButton()
{
AutoSizeAxes = Axes.Both;
}
protected override Drawable CreateContent() => content = new ButtonContent();
protected override void OnLoadStarted() => content.ToggleTextVisibility(false);
protected override void OnLoadFinished() => content.ToggleTextVisibility(true);
private class ButtonContent : CommentRepliesButton
{
public ButtonContent()
{
Text = "load replies";
}
}
}
}

View File

@ -0,0 +1,31 @@
// 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 Humanizer;
using osu.Framework.Bindables;
using osu.Framework.Input.Events;
namespace osu.Game.Overlays.Comments.Buttons
{
public class ShowRepliesButton : CommentRepliesButton
{
public readonly BindableBool Expanded = new BindableBool(true);
public ShowRepliesButton(int count)
{
Text = "reply".ToQuantity(count);
}
protected override void LoadComplete()
{
base.LoadComplete();
Expanded.BindValueChanged(expanded => SetIconDirection(expanded.NewValue), true);
}
protected override bool OnClick(ClickEvent e)
{
Expanded.Toggle();
return true;
}
}
}

View File

@ -86,7 +86,7 @@ namespace osu.Game.Overlays.Comments
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Text = @"Show deleted" Text = @"Show deleted"
} }
}, },

View File

@ -16,12 +16,12 @@ using System.Linq;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.Chat; using osu.Game.Online.Chat;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osuTK.Graphics;
using System.Collections.Generic; using System.Collections.Generic;
using System; using System;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using System.Collections.Specialized; using System.Collections.Specialized;
using osu.Game.Overlays.Comments.Buttons;
namespace osu.Game.Overlays.Comments namespace osu.Game.Overlays.Comments
{ {
@ -46,9 +46,9 @@ namespace osu.Game.Overlays.Comments
private FillFlowContainer childCommentsVisibilityContainer; private FillFlowContainer childCommentsVisibilityContainer;
private FillFlowContainer childCommentsContainer; private FillFlowContainer childCommentsContainer;
private LoadMoreCommentsButton loadMoreCommentsButton; private LoadRepliesButton loadRepliesButton;
private ShowMoreButton showMoreButton; private ShowMoreButton showMoreButton;
private RepliesButton repliesButton; private ShowRepliesButton showRepliesButton;
private ChevronButton chevronButton; private ChevronButton chevronButton;
private DeletedCommentsCounter deletedCommentsCounter; private DeletedCommentsCounter deletedCommentsCounter;
@ -81,7 +81,7 @@ namespace osu.Game.Overlays.Comments
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(margin) { Left = margin + 5 }, Padding = new MarginPadding(margin) { Left = margin + 5, Top = Comment.IsTopLevel ? 10 : 0 },
Child = content = new GridContainer Child = content = new GridContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
@ -163,26 +163,34 @@ namespace osu.Game.Overlays.Comments
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding { Right = 40 } Padding = new MarginPadding { Right = 40 }
}, },
info = new FillFlowContainer new FillFlowContainer
{ {
AutoSizeAxes = Axes.Both, AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal, Direction = FillDirection.Vertical,
Spacing = new Vector2(10, 0),
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText info = new FillFlowContainer
{ {
Anchor = Anchor.CentreLeft, AutoSizeAxes = Axes.Both,
Origin = Anchor.CentreLeft, Direction = FillDirection.Horizontal,
Font = OsuFont.GetFont(size: 12), Spacing = new Vector2(10, 0),
Colour = OsuColour.Gray(0.7f), Children = new Drawable[]
Text = HumanizerUtils.Humanize(Comment.CreatedAt) {
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12),
Colour = OsuColour.Gray(0.7f),
Text = HumanizerUtils.Humanize(Comment.CreatedAt)
},
}
}, },
repliesButton = new RepliesButton(Comment.RepliesCount) showRepliesButton = new ShowRepliesButton(Comment.RepliesCount)
{ {
Expanded = { BindTarget = childrenExpanded } Expanded = { BindTarget = childrenExpanded }
}, },
loadMoreCommentsButton = new LoadMoreCommentsButton loadRepliesButton = new LoadRepliesButton
{ {
Action = () => RepliesRequested(this, ++currentPage) Action = () => RepliesRequested(this, ++currentPage)
} }
@ -339,14 +347,14 @@ namespace osu.Game.Overlays.Comments
var loadedReplesCount = loadedReplies.Count; var loadedReplesCount = loadedReplies.Count;
var hasUnloadedReplies = loadedReplesCount != Comment.RepliesCount; var hasUnloadedReplies = loadedReplesCount != Comment.RepliesCount;
loadMoreCommentsButton.FadeTo(hasUnloadedReplies && loadedReplesCount == 0 ? 1 : 0); loadRepliesButton.FadeTo(hasUnloadedReplies && loadedReplesCount == 0 ? 1 : 0);
showMoreButton.FadeTo(hasUnloadedReplies && loadedReplesCount > 0 ? 1 : 0); showMoreButton.FadeTo(hasUnloadedReplies && loadedReplesCount > 0 ? 1 : 0);
repliesButton.FadeTo(loadedReplesCount != 0 ? 1 : 0); showRepliesButton.FadeTo(loadedReplesCount != 0 ? 1 : 0);
if (Comment.IsTopLevel) if (Comment.IsTopLevel)
chevronButton.FadeTo(loadedReplesCount != 0 ? 1 : 0); chevronButton.FadeTo(loadedReplesCount != 0 ? 1 : 0);
showMoreButton.IsLoading = loadMoreCommentsButton.IsLoading = false; showMoreButton.IsLoading = loadRepliesButton.IsLoading = false;
} }
private class ChevronButton : ShowChildrenButton private class ChevronButton : ShowChildrenButton
@ -367,38 +375,6 @@ namespace osu.Game.Overlays.Comments
} }
} }
private class RepliesButton : ShowChildrenButton
{
private readonly SpriteText text;
private readonly int count;
public RepliesButton(int count)
{
this.count = count;
Child = text = new OsuSpriteText
{
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
};
}
protected override void OnExpandedChanged(ValueChangedEvent<bool> expanded)
{
text.Text = $@"{(expanded.NewValue ? "[-]" : "[+]")} replies ({count})";
}
}
private class LoadMoreCommentsButton : GetCommentRepliesButton
{
public LoadMoreCommentsButton()
{
IdleColour = OsuColour.Gray(0.7f);
HoverColour = Color4.White;
}
protected override string GetText() => @"[+] load replies";
}
private class ShowMoreButton : GetCommentRepliesButton private class ShowMoreButton : GetCommentRepliesButton
{ {
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -132,7 +132,7 @@ namespace osu.Game.Overlays.Mods
{ {
foreach (var button in buttons) foreach (var button in buttons)
{ {
int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t.IsInstanceOfType(m))); int i = Array.FindIndex(button.Mods, m => modTypes.Any(t => t == m.GetType()));
if (i >= 0) if (i >= 0)
button.SelectAt(i); button.SelectAt(i);

View File

@ -19,6 +19,7 @@ using osu.Game.Graphics.Backgrounds;
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.Input.Bindings;
using osu.Game.Overlays.Mods.Sections; using osu.Game.Overlays.Mods.Sections;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Screens; using osu.Game.Screens;
@ -403,6 +404,8 @@ namespace osu.Game.Overlays.Mods
return base.OnKeyDown(e); return base.OnKeyDown(e);
} }
public override bool OnPressed(GlobalAction action) => false; // handled by back button
private void availableModsChanged(ValueChangedEvent<Dictionary<ModType, IReadOnlyList<Mod>>> mods) private void availableModsChanged(ValueChangedEvent<Dictionary<ModType, IReadOnlyList<Mod>>> mods)
{ {
if (mods.NewValue == null) return; if (mods.NewValue == null) return;

View File

@ -149,7 +149,7 @@ namespace osu.Game.Overlays
if (beatmap.Disabled) if (beatmap.Disabled)
return; return;
next(); NextTrack();
} }
else if (!IsPlaying) else if (!IsPlaying)
{ {
@ -217,6 +217,9 @@ namespace osu.Game.Overlays
/// <returns>The <see cref="PreviousTrackResult"/> that indicate the decided action.</returns> /// <returns>The <see cref="PreviousTrackResult"/> that indicate the decided action.</returns>
private PreviousTrackResult prev() private PreviousTrackResult prev()
{ {
if (beatmap.Disabled)
return PreviousTrackResult.None;
var currentTrackPosition = current?.Track.CurrentTime; var currentTrackPosition = current?.Track.CurrentTime;
if (currentTrackPosition >= restart_cutoff_point) if (currentTrackPosition >= restart_cutoff_point)
@ -248,6 +251,9 @@ namespace osu.Game.Overlays
private bool next() private bool next()
{ {
if (beatmap.Disabled)
return false;
queuedDirection = TrackChangeDirection.Next; queuedDirection = TrackChangeDirection.Next;
var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).ElementAtOrDefault(1) ?? BeatmapSets.FirstOrDefault(); var playable = BeatmapSets.SkipWhile(i => i.ID != current.BeatmapSetInfo.ID).ElementAtOrDefault(1) ?? BeatmapSets.FirstOrDefault();

View File

@ -30,6 +30,14 @@ namespace osu.Game.Overlays
set => current.Current = value; set => current.Current = value;
} }
public string Title
{
get => text.Text;
set => text.Text = value;
}
private readonly OsuSpriteText text;
public OverlaySortTabControl() public OverlaySortTabControl()
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
@ -40,11 +48,11 @@ namespace osu.Game.Overlays
Spacing = new Vector2(10, 0), Spacing = new Vector2(10, 0),
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText text = new OsuSpriteText
{ {
Anchor = Anchor.CentreLeft, Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft, Origin = Anchor.CentreLeft,
Font = OsuFont.GetFont(size: 12), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Text = @"Sort by" Text = @"Sort by"
}, },
CreateControl().With(c => CreateControl().With(c =>
@ -133,7 +141,7 @@ namespace osu.Game.Overlays
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Font = OsuFont.GetFont(size: 12), Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold),
Text = (value as Enum)?.GetDescription() ?? value.ToString() Text = (value as Enum)?.GetDescription() ?? value.ToString()
} }
} }
@ -163,7 +171,7 @@ namespace osu.Game.Overlays
ContentColour = Active.Value && !IsHovered ? colourProvider.Light1 : Color4.White; ContentColour = Active.Value && !IsHovered ? colourProvider.Light1 : Color4.White;
text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.Medium); text.Font = text.Font.With(weight: Active.Value ? FontWeight.Bold : FontWeight.SemiBold);
} }
} }
} }

View File

@ -0,0 +1,19 @@
// 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.
namespace osu.Game.Overlays.Rankings
{
public class RankingsSortTabControl : OverlaySortTabControl<RankingsSortCriteria>
{
public RankingsSortTabControl()
{
Title = "Show";
}
}
public enum RankingsSortCriteria
{
All,
Friends
}
}

View File

@ -22,10 +22,8 @@ namespace osu.Game.Overlays.Rankings
{ {
private const int duration = 300; private const int duration = 300;
private readonly Box background;
private readonly SpotlightsDropdown dropdown;
private readonly BindableWithCurrent<APISpotlight> current = new BindableWithCurrent<APISpotlight>(); private readonly BindableWithCurrent<APISpotlight> current = new BindableWithCurrent<APISpotlight>();
public readonly Bindable<RankingsSortCriteria> Sort = new Bindable<RankingsSortCriteria>();
public Bindable<APISpotlight> Current public Bindable<APISpotlight> Current
{ {
@ -41,19 +39,22 @@ namespace osu.Game.Overlays.Rankings
protected override bool StartHidden => true; protected override bool StartHidden => true;
private readonly Box background;
private readonly Container content;
private readonly SpotlightsDropdown dropdown;
private readonly InfoColumn startDateColumn; private readonly InfoColumn startDateColumn;
private readonly InfoColumn endDateColumn; private readonly InfoColumn endDateColumn;
private readonly InfoColumn mapCountColumn; private readonly InfoColumn mapCountColumn;
private readonly InfoColumn participantsColumn; private readonly InfoColumn participantsColumn;
private readonly Container content;
public SpotlightSelector() public SpotlightSelector()
{ {
RelativeSizeAxes = Axes.X; RelativeSizeAxes = Axes.X;
Height = 100; AutoSizeAxes = Axes.Y;
Add(content = new Container Add(content = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[] Children = new Drawable[]
{ {
background = new Box background = new Box
@ -62,31 +63,55 @@ namespace osu.Game.Overlays.Rankings
}, },
new Container new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN, Vertical = 10 }, AutoSizeAxes = Axes.Y,
Children = new Drawable[] Padding = new MarginPadding { Horizontal = UserProfileOverlay.CONTENT_X_MARGIN },
Child = new FillFlowContainer
{ {
dropdown = new SpotlightsDropdown RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
Anchor = Anchor.TopCentre, new Container
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Current = Current,
Depth = -float.MaxValue
},
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(15, 0),
Children = new Drawable[]
{ {
startDateColumn = new InfoColumn(@"Start Date"), Margin = new MarginPadding { Vertical = 20 },
endDateColumn = new InfoColumn(@"End Date"), RelativeSizeAxes = Axes.X,
mapCountColumn = new InfoColumn(@"Map Count"), Height = 40,
participantsColumn = new InfoColumn(@"Participants") Depth = -float.MaxValue,
Child = dropdown = new SpotlightsDropdown
{
RelativeSizeAxes = Axes.X,
Current = Current
}
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Margin = new MarginPadding { Bottom = 5 },
Children = new Drawable[]
{
startDateColumn = new InfoColumn(@"Start Date"),
endDateColumn = new InfoColumn(@"End Date"),
mapCountColumn = new InfoColumn(@"Map Count"),
participantsColumn = new InfoColumn(@"Participants")
}
},
new RankingsSortTabControl
{
Anchor = Anchor.CentreRight,
Origin = Anchor.CentreRight,
Current = Sort
}
}
} }
} }
} }
@ -128,12 +153,13 @@ namespace osu.Game.Overlays.Rankings
{ {
AutoSizeAxes = Axes.Both; AutoSizeAxes = Axes.Both;
Direction = FillDirection.Vertical; Direction = FillDirection.Vertical;
Margin = new MarginPadding { Vertical = 10 };
Children = new Drawable[] Children = new Drawable[]
{ {
new OsuSpriteText new OsuSpriteText
{ {
Text = name, Text = name,
Font = OsuFont.GetFont(size: 10), Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular),
}, },
new Container new Container
{ {
@ -143,7 +169,7 @@ namespace osu.Game.Overlays.Rankings
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
Font = OsuFont.GetFont(size: 18, weight: FontWeight.Light), Font = OsuFont.GetFont(size: 20, weight: FontWeight.Light),
} }
} }
}; };

View File

@ -24,6 +24,7 @@ namespace osu.Game.Overlays.Rankings
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>(); public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private readonly Bindable<APISpotlight> selectedSpotlight = new Bindable<APISpotlight>(); private readonly Bindable<APISpotlight> selectedSpotlight = new Bindable<APISpotlight>();
private readonly Bindable<RankingsSortCriteria> sort = new Bindable<RankingsSortCriteria>();
[Resolved] [Resolved]
private IAPIProvider api { get; set; } private IAPIProvider api { get; set; }
@ -72,6 +73,8 @@ namespace osu.Game.Overlays.Rankings
} }
} }
}; };
sort.BindTo(selector.Sort);
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -80,7 +83,8 @@ namespace osu.Game.Overlays.Rankings
selector.Show(); selector.Show();
selectedSpotlight.BindValueChanged(onSpotlightChanged); selectedSpotlight.BindValueChanged(_ => onSpotlightChanged());
sort.BindValueChanged(_ => onSpotlightChanged());
Ruleset.BindValueChanged(onRulesetChanged); Ruleset.BindValueChanged(onRulesetChanged);
getSpotlights(); getSpotlights();
@ -101,14 +105,14 @@ namespace osu.Game.Overlays.Rankings
selectedSpotlight.TriggerChange(); selectedSpotlight.TriggerChange();
} }
private void onSpotlightChanged(ValueChangedEvent<APISpotlight> spotlight) private void onSpotlightChanged()
{ {
loading.Show(); loading.Show();
cancellationToken?.Cancel(); cancellationToken?.Cancel();
getRankingsRequest?.Cancel(); getRankingsRequest?.Cancel();
getRankingsRequest = new GetSpotlightRankingsRequest(Ruleset.Value, spotlight.NewValue.Id); getRankingsRequest = new GetSpotlightRankingsRequest(Ruleset.Value, selectedSpotlight.Value.Id, sort.Value);
getRankingsRequest.Success += onSuccess; getRankingsRequest.Success += onSuccess;
api.Queue(getRankingsRequest); api.Queue(getRankingsRequest);
} }

View File

@ -31,8 +31,10 @@ namespace osu.Game.Rulesets.Judgements
public JudgementResult Result { get; private set; } public JudgementResult Result { get; private set; }
public DrawableHitObject JudgedObject { get; private set; } public DrawableHitObject JudgedObject { get; private set; }
protected Container JudgementBody; protected Container JudgementBody { get; private set; }
protected SpriteText JudgementText; protected SpriteText JudgementText { get; private set; }
private SkinnableDrawable bodyDrawable;
/// <summary> /// <summary>
/// Duration of initial fade in. /// Duration of initial fade in.
@ -89,6 +91,8 @@ namespace osu.Game.Rulesets.Judgements
prepareDrawables(); prepareDrawables();
bodyDrawable.ResetAnimation();
this.FadeInFromZero(FadeInDuration, Easing.OutQuint); this.FadeInFromZero(FadeInDuration, Easing.OutQuint);
JudgementBody.ScaleTo(1); JudgementBody.ScaleTo(1);
JudgementBody.RotateTo(0); JudgementBody.RotateTo(0);
@ -131,7 +135,7 @@ namespace osu.Game.Rulesets.Judgements
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Child = new SkinnableDrawable(new GameplaySkinComponent<HitResult>(type), _ => JudgementText = new OsuSpriteText Child = bodyDrawable = new SkinnableDrawable(new GameplaySkinComponent<HitResult>(type), _ => JudgementText = new OsuSpriteText
{ {
Text = type.GetDescription().ToUpperInvariant(), Text = type.GetDescription().ToUpperInvariant(),
Font = OsuFont.Numeric.With(size: 20), Font = OsuFont.Numeric.With(size: 20),

View File

@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Mods
public BindableNumber<float> DrainRate { get; } = new BindableFloat public BindableNumber<float> DrainRate { get; } = new BindableFloat
{ {
Precision = 0.1f, Precision = 0.1f,
MinValue = 1, MinValue = 0,
MaxValue = 10, MaxValue = 10,
Default = 5, Default = 5,
Value = 5, Value = 5,
@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mods
public BindableNumber<float> OverallDifficulty { get; } = new BindableFloat public BindableNumber<float> OverallDifficulty { get; } = new BindableFloat
{ {
Precision = 0.1f, Precision = 0.1f,
MinValue = 1, MinValue = 0,
MaxValue = 10, MaxValue = 10,
Default = 5, Default = 5,
Value = 5, Value = 5,

View File

@ -147,8 +147,9 @@ namespace osu.Game.Rulesets.Objects.Drawables
samplesBindable = HitObject.SamplesBindable.GetBoundCopy(); samplesBindable = HitObject.SamplesBindable.GetBoundCopy();
samplesBindable.CollectionChanged += (_, __) => loadSamples(); samplesBindable.CollectionChanged += (_, __) => loadSamples();
updateState(ArmedState.Idle, true);
apply(HitObject); apply(HitObject);
updateState(ArmedState.Idle, true);
} }
private void loadSamples() private void loadSamples()

View File

@ -7,6 +7,7 @@ using System.Diagnostics;
using System.Linq; using System.Linq;
using osu.Framework.Bindables; using osu.Framework.Bindables;
using osu.Framework.Extensions; using osu.Framework.Extensions;
using osu.Framework.Utils;
using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
@ -16,8 +17,6 @@ namespace osu.Game.Rulesets.Scoring
{ {
public class ScoreProcessor : JudgementProcessor public class ScoreProcessor : JudgementProcessor
{ {
private const double base_portion = 0.3;
private const double combo_portion = 0.7;
private const double max_score = 1000000; private const double max_score = 1000000;
/// <summary> /// <summary>
@ -55,8 +54,20 @@ namespace osu.Game.Rulesets.Scoring
/// </summary> /// </summary>
public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>(); public readonly Bindable<ScoringMode> Mode = new Bindable<ScoringMode>();
private double maxHighestCombo; /// <summary>
/// The default portion of <see cref="max_score"/> awarded for hitting <see cref="HitObject"/>s accurately. Defaults to 30%.
/// </summary>
protected virtual double DefaultAccuracyPortion => 0.3;
/// <summary>
/// The default portion of <see cref="max_score"/> awarded for achieving a high combo. Default to 70%.
/// </summary>
protected virtual double DefaultComboPortion => 0.7;
private readonly double accuracyPortion;
private readonly double comboPortion;
private double maxHighestCombo;
private double maxBaseScore; private double maxBaseScore;
private double rollingMaxBaseScore; private double rollingMaxBaseScore;
private double baseScore; private double baseScore;
@ -69,7 +80,11 @@ namespace osu.Game.Rulesets.Scoring
public ScoreProcessor() public ScoreProcessor()
{ {
Debug.Assert(base_portion + combo_portion == 1.0); accuracyPortion = DefaultAccuracyPortion;
comboPortion = DefaultComboPortion;
if (!Precision.AlmostEquals(1.0, accuracyPortion + comboPortion))
throw new InvalidOperationException($"{nameof(DefaultAccuracyPortion)} + {nameof(DefaultComboPortion)} must equal 1.");
Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue);
Accuracy.ValueChanged += accuracy => Accuracy.ValueChanged += accuracy =>
@ -189,7 +204,10 @@ namespace osu.Game.Rulesets.Scoring
{ {
default: default:
case ScoringMode.Standardised: case ScoringMode.Standardised:
return (max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo.Value / maxHighestCombo) + bonusScore) * scoreMultiplier; double accuracyScore = accuracyPortion * baseScore / maxBaseScore;
double comboScore = comboPortion * HighestCombo.Value / maxHighestCombo;
return (max_score * (accuracyScore + comboScore) + bonusScore) * scoreMultiplier;
case ScoringMode.Classic: case ScoringMode.Classic:
// should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1) // should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1)

View File

@ -21,12 +21,10 @@ namespace osu.Game.Screens.Backgrounds
private int currentDisplay; private int currentDisplay;
private const int background_count = 7; private const int background_count = 7;
private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}";
private Bindable<User> user; private Bindable<User> user;
private Bindable<Skin> skin; private Bindable<Skin> skin;
private Bindable<BackgroundSource> mode; private Bindable<BackgroundSource> mode;
private Bindable<IntroSequence> introSequence;
[Resolved] [Resolved]
private IBindable<WorkingBeatmap> beatmap { get; set; } private IBindable<WorkingBeatmap> beatmap { get; set; }
@ -42,11 +40,13 @@ namespace osu.Game.Screens.Backgrounds
user = api.LocalUser.GetBoundCopy(); user = api.LocalUser.GetBoundCopy();
skin = skinManager.CurrentSkin.GetBoundCopy(); skin = skinManager.CurrentSkin.GetBoundCopy();
mode = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource); mode = config.GetBindable<BackgroundSource>(OsuSetting.MenuBackgroundSource);
introSequence = config.GetBindable<IntroSequence>(OsuSetting.IntroSequence);
user.ValueChanged += _ => Next(); user.ValueChanged += _ => Next();
skin.ValueChanged += _ => Next(); skin.ValueChanged += _ => Next();
mode.ValueChanged += _ => Next(); mode.ValueChanged += _ => Next();
beatmap.ValueChanged += _ => Next(); beatmap.ValueChanged += _ => Next();
introSequence.ValueChanged += _ => Next();
currentDisplay = RNG.Next(0, background_count); currentDisplay = RNG.Next(0, background_count);
@ -73,6 +73,18 @@ namespace osu.Game.Screens.Backgrounds
private Background createBackground() private Background createBackground()
{ {
Background newBackground; Background newBackground;
string backgroundName;
switch (introSequence.Value)
{
case IntroSequence.Welcome:
backgroundName = "Intro/Welcome/menu-background";
break;
default:
backgroundName = $@"Menu/menu-background-{currentDisplay % background_count + 1}";
break;
}
if (user.Value?.IsSupporter ?? false) if (user.Value?.IsSupporter ?? false)
{ {

View File

@ -56,5 +56,14 @@ namespace osu.Game.Screens
/// Whether mod rate adjustments are allowed to be applied. /// Whether mod rate adjustments are allowed to be applied.
/// </summary> /// </summary>
bool AllowRateAdjustments { get; } bool AllowRateAdjustments { get; }
/// <summary>
/// Invoked when the back button has been pressed to close any overlays before exiting this <see cref="IOsuScreen"/>.
/// </summary>
/// <remarks>
/// Return <c>true</c> to block this <see cref="IOsuScreen"/> from being exited after closing an overlay.
/// Return <c>false</c> if this <see cref="IOsuScreen"/> should continue exiting.
/// </remarks>
bool OnBackButton();
} }
} }

View File

@ -3,7 +3,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio; using osu.Framework.Audio;
using osu.Framework.Audio.Sample; using osu.Framework.Audio.Sample;
@ -12,7 +11,6 @@ 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.Textures; using osu.Framework.Graphics.Textures;
using osu.Framework.Graphics.Video;
using osu.Framework.Utils; using osu.Framework.Utils;
using osu.Framework.Timing; using osu.Framework.Timing;
using osu.Game.Graphics; using osu.Game.Graphics;
@ -88,7 +86,7 @@ namespace osu.Game.Screens.Menu
private RulesetFlow rulesets; private RulesetFlow rulesets;
private Container rulesetsScale; private Container rulesetsScale;
private Container logoContainerSecondary; private Container logoContainerSecondary;
private Drawable lazerLogo; private LazerLogo lazerLogo;
private GlitchingTriangles triangles; private GlitchingTriangles triangles;
@ -139,10 +137,10 @@ namespace osu.Game.Screens.Menu
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre,
Child = lazerLogo = new LazerLogo(textures.GetStream("Menu/logo-triangles.mp4")) Child = lazerLogo = new LazerLogo
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.Centre
} }
}, },
}; };
@ -218,6 +216,9 @@ namespace osu.Game.Screens.Menu
// matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5 // matching flyte curve y = 0.25x^2 + (max(0, x - 0.7) / 0.3) ^ 5
lazerLogo.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint); lazerLogo.FadeIn().ScaleTo(scale_start).Then().Delay(logo_scale_duration * 0.7f).ScaleTo(scale_start - scale_adjust, logo_scale_duration * 0.3f, Easing.InQuint);
lazerLogo.TransformTo(nameof(LazerLogo.Progress), 1f, logo_scale_duration);
logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad); logoContainerSecondary.ScaleTo(scale_start).Then().ScaleTo(scale_start - scale_adjust * 0.25f, logo_scale_duration, Easing.InQuad);
} }
@ -259,20 +260,40 @@ namespace osu.Game.Screens.Menu
private class LazerLogo : CompositeDrawable private class LazerLogo : CompositeDrawable
{ {
private readonly Stream videoStream; private HueAnimation highlight, background;
public LazerLogo(Stream videoStream) public float Progress
{
get => background.AnimationProgress;
set
{
background.AnimationProgress = value;
highlight.AnimationProgress = value;
}
}
public LazerLogo()
{ {
this.videoStream = videoStream;
Size = new Vector2(960); Size = new Vector2(960);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(TextureStore textures)
{ {
InternalChild = new Video(videoStream) InternalChildren = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, highlight = new HueAnimation
{
RelativeSizeAxes = Axes.Both,
Texture = textures.Get(@"Intro/Triangles/logo-highlight"),
Colour = Color4.White,
},
background = new HueAnimation
{
RelativeSizeAxes = Axes.Both,
Texture = textures.Get(@"Intro/Triangles/logo-background"),
Colour = OsuColour.Gray(0.6f),
},
}; };
} }
} }

View File

@ -219,6 +219,8 @@ namespace osu.Game.Screens.Multi.Lounge.Components
Alpha = 0; Alpha = 0;
} }
protected override bool ShouldBeConsideredForInput(Drawable child) => state == SelectionState.Selected;
private class RoomName : OsuSpriteText private class RoomName : OsuSpriteText
{ {
[Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Name))] [Resolved(typeof(Room), nameof(Online.Multiplayer.Room.Name))]

View File

@ -250,12 +250,6 @@ namespace osu.Game.Screens.Multi
{ {
roomManager.PartRoom(); roomManager.PartRoom();
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
{
screenStack.Exit();
return true;
}
waves.Hide(); waves.Hide();
this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut(); this.Delay(WaveContainer.DISAPPEAR_DURATION).FadeOut();
@ -269,6 +263,20 @@ namespace osu.Game.Screens.Multi
return false; return false;
} }
public override bool OnBackButton()
{
if ((screenStack.CurrentScreen as IMultiplayerSubScreen)?.OnBackButton() == true)
return true;
if (screenStack.CurrentScreen != null && !(screenStack.CurrentScreen is LoungeSubScreen))
{
screenStack.Exit();
return true;
}
return false;
}
protected override void LogoExiting(OsuLogo logo) protected override void LogoExiting(OsuLogo logo)
{ {
base.LogoExiting(logo); base.LogoExiting(logo);

View File

@ -114,7 +114,7 @@ namespace osu.Game.Screens.Multi
public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) public void JoinRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{ {
currentJoinRoomRequest?.Cancel(); currentJoinRoomRequest?.Cancel();
currentJoinRoomRequest = new JoinRoomRequest(room, api.LocalUser.Value); currentJoinRoomRequest = new JoinRoomRequest(room);
currentJoinRoomRequest.Success += () => currentJoinRoomRequest.Success += () =>
{ {
@ -139,7 +139,7 @@ namespace osu.Game.Screens.Multi
if (joinedRoom == null) if (joinedRoom == null)
return; return;
api.Queue(new PartRoomRequest(joinedRoom, api.LocalUser.Value)); api.Queue(new PartRoomRequest(joinedRoom));
joinedRoom = null; joinedRoom = null;
} }

View File

@ -258,5 +258,7 @@ namespace osu.Game.Screens
/// Note that the instance created may not be the used instance if it matches the BackgroundMode equality clause. /// Note that the instance created may not be the used instance if it matches the BackgroundMode equality clause.
/// </summary> /// </summary>
protected virtual BackgroundScreen CreateBackground() => null; protected virtual BackgroundScreen CreateBackground() => null;
public virtual bool OnBackButton() => false;
} }
} }

View File

@ -5,6 +5,7 @@ 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.Audio; using osu.Game.Audio;
using osu.Game.Configuration;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Skinning; using osu.Game.Skinning;
@ -16,27 +17,34 @@ namespace osu.Game.Screens.Play
private SkinnableSound comboBreakSample; private SkinnableSound comboBreakSample;
private Bindable<bool> alwaysPlay;
private bool firstTime = true;
public ComboEffects(ScoreProcessor processor) public ComboEffects(ScoreProcessor processor)
{ {
this.processor = processor; this.processor = processor;
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(OsuConfigManager config)
{ {
InternalChild = comboBreakSample = new SkinnableSound(new SampleInfo("combobreak")); InternalChild = comboBreakSample = new SkinnableSound(new SampleInfo("combobreak"));
alwaysPlay = config.GetBindable<bool>(OsuSetting.AlwaysPlayFirstComboBreak);
} }
protected override void LoadComplete() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
processor.Combo.BindValueChanged(onComboChange, true); processor.Combo.BindValueChanged(onComboChange);
} }
private void onComboChange(ValueChangedEvent<int> combo) private void onComboChange(ValueChangedEvent<int> combo)
{ {
if (combo.NewValue == 0 && combo.OldValue > 20) if (combo.NewValue == 0 && (combo.OldValue > 20 || (alwaysPlay.Value && firstTime)))
{
comboBreakSample?.Play(); comboBreakSample?.Play();
firstTime = false;
}
} }
} }
} }

View File

@ -251,6 +251,7 @@ namespace osu.Game.Screens.Play.HUD
switch (action) switch (action)
{ {
case GlobalAction.Back: case GlobalAction.Back:
case GlobalAction.PauseGameplay: // in the future this behaviour will differ for replays etc.
if (!pendingAnimation) if (!pendingAnimation)
BeginConfirm(); BeginConfirm();
return true; return true;
@ -264,6 +265,7 @@ namespace osu.Game.Screens.Play.HUD
switch (action) switch (action)
{ {
case GlobalAction.Back: case GlobalAction.Back:
case GlobalAction.PauseGameplay:
AbortConfirm(); AbortConfirm();
break; break;
} }

View File

@ -57,6 +57,8 @@ namespace osu.Game.Screens.Play
set => CurrentNumber.Value = value; set => CurrentNumber.Value = value;
} }
protected override bool AllowKeyboardInputWhenNotHovered => true;
public SongProgressBar(float barHeight, float handleBarHeight, Vector2 handleSize) public SongProgressBar(float barHeight, float handleBarHeight, Vector2 handleSize)
{ {
CurrentNumber.MinValue = 0; CurrentNumber.MinValue = 0;

View File

@ -77,11 +77,10 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
Origin = Anchor.Centre, Origin = Anchor.Centre,
BlurSigma = new Vector2(35), BlurSigma = new Vector2(35),
BypassAutoSizeAxes = Axes.Both, BypassAutoSizeAxes = Axes.Both,
RelativeSizeAxes = Axes.Both, Size = new Vector2(200),
CacheDrawnFrameBuffer = true, CacheDrawnFrameBuffer = true,
Blending = BlendingParameters.Additive, Blending = BlendingParameters.Additive,
Alpha = 0, Alpha = 0,
Size = new Vector2(2f), // increase buffer size to allow for scale
Scale = new Vector2(1.8f), Scale = new Vector2(1.8f),
Children = new[] Children = new[]
{ {
@ -122,15 +121,18 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy
} }
flash.Colour = OsuColour.ForRank(rank); flash.Colour = OsuColour.ForRank(rank);
flash.FadeIn().Then().FadeOut(1200, Easing.OutQuint);
if (rank >= ScoreRank.S) if (rank >= ScoreRank.S)
rankText.ScaleTo(1.05f).ScaleTo(1, 3000, Easing.OutQuint); rankText.ScaleTo(1.05f).ScaleTo(1, 3000, Easing.OutQuint);
if (rank >= ScoreRank.X) if (rank >= ScoreRank.X)
{ {
flash.FadeIn().Then().FadeOut(3000); flash.FadeOutFromOne(3000);
superFlash.FadeIn().Then().FadeOut(800, Easing.OutQuint); superFlash.FadeOutFromOne(800, Easing.OutQuint);
}
else
{
flash.FadeOutFromOne(1200, Easing.OutQuint);
} }
} }
} }

View File

@ -194,6 +194,13 @@ namespace osu.Game.Screens.Ranking
} }
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{
Background.FadeTo(1, 250);
return base.OnExiting(next);
}
public override bool OnBackButton()
{ {
if (statisticsPanel.State.Value == Visibility.Visible) if (statisticsPanel.State.Value == Visibility.Visible)
{ {
@ -201,9 +208,7 @@ namespace osu.Game.Screens.Ranking
return true; return true;
} }
Background.FadeTo(1, 250); return false;
return base.OnExiting(next);
} }
private void addScore(ScoreInfo score) private void addScore(ScoreInfo score)

View File

@ -95,7 +95,6 @@ namespace osu.Game.Screens.Select
CarouselRoot newRoot = new CarouselRoot(this); CarouselRoot newRoot = new CarouselRoot(this);
beatmapSets.Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild); beatmapSets.Select(createCarouselSet).Where(g => g != null).ForEach(newRoot.AddChild);
newRoot.Filter(activeCriteria);
// preload drawables as the ctor overhead is quite high currently. // preload drawables as the ctor overhead is quite high currently.
_ = newRoot.Drawables; _ = newRoot.Drawables;
@ -108,6 +107,9 @@ namespace osu.Game.Screens.Select
itemsCache.Invalidate(); itemsCache.Invalidate();
scrollPositionCache.Invalidate(); scrollPositionCache.Invalidate();
// apply any pending filter operation that may have been delayed (see applyActiveCriteria's scheduling behaviour when BeatmapSetsLoaded is false).
FlushPendingFilterOperations();
// Run on late scheduler want to ensure this runs after all pending UpdateBeatmapSet / RemoveBeatmapSet operations are run. // Run on late scheduler want to ensure this runs after all pending UpdateBeatmapSet / RemoveBeatmapSet operations are run.
SchedulerAfterChildren.Add(() => SchedulerAfterChildren.Add(() =>
{ {
@ -321,6 +323,9 @@ namespace osu.Game.Screens.Select
/// <returns>True if a selection could be made, else False.</returns> /// <returns>True if a selection could be made, else False.</returns>
public bool SelectNextRandom() public bool SelectNextRandom()
{ {
if (!AllowSelection)
return false;
var visibleSets = beatmapSets.Where(s => !s.Filtered.Value).ToList(); var visibleSets = beatmapSets.Where(s => !s.Filtered.Value).ToList();
if (!visibleSets.Any()) if (!visibleSets.Any())
return false; return false;
@ -427,7 +432,19 @@ namespace osu.Game.Screens.Select
private void applyActiveCriteria(bool debounce, bool alwaysResetScrollPosition = true) private void applyActiveCriteria(bool debounce, bool alwaysResetScrollPosition = true)
{ {
if (root.Children.Any() != true) return; PendingFilter?.Cancel();
PendingFilter = null;
if (debounce)
PendingFilter = Scheduler.AddDelayed(perform, 250);
else
{
// if initial load is not yet finished, this will be run inline in loadBeatmapSets to ensure correct order of operation.
if (!BeatmapSetsLoaded)
PendingFilter = Schedule(perform);
else
perform();
}
void perform() void perform()
{ {
@ -439,14 +456,6 @@ namespace osu.Game.Screens.Select
if (alwaysResetScrollPosition || !scroll.UserScrolling) if (alwaysResetScrollPosition || !scroll.UserScrolling)
ScrollToSelected(); ScrollToSelected();
} }
PendingFilter?.Cancel();
PendingFilter = null;
if (debounce)
PendingFilter = Scheduler.AddDelayed(perform, 250);
else
perform();
} }
private float? scrollTarget; private float? scrollTarget;

View File

@ -599,12 +599,6 @@ namespace osu.Game.Screens.Select
public override bool OnExiting(IScreen next) public override bool OnExiting(IScreen next)
{ {
if (ModSelect.State.Value == Visibility.Visible)
{
ModSelect.Hide();
return true;
}
if (base.OnExiting(next)) if (base.OnExiting(next))
return true; return true;
@ -620,6 +614,17 @@ namespace osu.Game.Screens.Select
return false; return false;
} }
public override bool OnBackButton()
{
if (ModSelect.State.Value == Visibility.Visible)
{
ModSelect.Hide();
return true;
}
return false;
}
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);

View File

@ -4,6 +4,7 @@
using System; using System;
using osu.Framework.Caching; using osu.Framework.Caching;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Animations;
using osuTK; using osuTK;
namespace osu.Game.Skinning namespace osu.Game.Skinning
@ -50,6 +51,11 @@ namespace osu.Game.Skinning
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
} }
/// <summary>
/// Seeks to the 0-th frame if the content of this <see cref="SkinnableDrawable"/> is an <see cref="IFramedAnimation"/>.
/// </summary>
public void ResetAnimation() => (Drawable as IFramedAnimation)?.GotoFrame(0);
private readonly Func<ISkinComponent, Drawable> createDefault; private readonly Func<ISkinComponent, Drawable> createDefault;
private readonly Cached scaling = new Cached(); private readonly Cached scaling = new Cached();

View File

@ -55,10 +55,11 @@ namespace osu.Game.Storyboards.Drawables
if (video == null) return; if (video == null) return;
video.PlaybackPosition = Clock.CurrentTime - Video.StartTime; using (video.BeginAbsoluteSequence(Video.StartTime))
{
using (video.BeginAbsoluteSequence(0)) Schedule(() => video.PlaybackPosition = Time.Current - Video.StartTime);
video.FadeIn(500); video.FadeIn(500);
}
} }
} }
} }

View File

@ -24,8 +24,8 @@
<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.710.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.714.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.622.1" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.715.0" />
<PackageReference Include="Sentry" Version="2.1.4" /> <PackageReference Include="Sentry" Version="2.1.4" />
<PackageReference Include="SharpCompress" Version="0.25.1" /> <PackageReference Include="SharpCompress" Version="0.25.1" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -70,8 +70,8 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.710.0" /> <PackageReference Include="ppy.osu.Framework.iOS" Version="2020.714.1" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.622.1" /> <PackageReference Include="ppy.osu.Game.Resources" Version="2020.715.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">
@ -80,7 +80,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.710.0" /> <PackageReference Include="ppy.osu.Framework" Version="2020.714.1" />
<PackageReference Include="SharpCompress" Version="0.25.1" /> <PackageReference Include="SharpCompress" Version="0.25.1" />
<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" />