Merge remote-tracking branch 'upstream/master' into android

This commit is contained in:
tangalbert919 2019-01-23 17:45:46 -06:00
commit e3ce203c68
89 changed files with 836 additions and 525 deletions

View File

@ -11,25 +11,71 @@ We are accepting bug reports (please report with as much detail as possible). Fe
# Requirements # Requirements
- A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed. - A desktop platform with the [.NET Core SDK 2.2](https://www.microsoft.com/net/learn/get-started) or higher installed.
- When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio Community Edition](https://www.visualstudio.com/) (Windows), [Visual Studio Code](https://code.visualstudio.com/) (with the C# plugin installed) or [Jetbrains Rider](https://www.jetbrains.com/rider/) (commercial). - When working with the codebase, we recommend using an IDE with intellisense and syntax highlighting, such as [Visual Studio 2017+](https://visualstudio.microsoft.com/vs/), [Jetbrains Rider](https://www.jetbrains.com/rider/) or [Visual Studio Code](https://code.visualstudio.com/).
- Note that there are **[additional requirements for Windows 7 and Windows 8.1](https://docs.microsoft.com/en-us/dotnet/core/windows-prerequisites?tabs=netcore2x)** which you may need to manually install if your operating system is not up-to-date.
# Building and running # Running osu!
If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled (download and run the install executable for your platform). ## Releases
Clone the repository including submodules If you are not interested in developing the game, please head over to the [releases](https://github.com/ppy/osu/releases) to download a precompiled build with automatic updating enabled.
`git clone --recurse-submodules https://github.com/ppy/osu` - Windows (x64) users should download and run `install.exe`.
- macOS users (10.12 "Sierra" and higher) should download and run `osu.app.zip`.
- iOS users can join the [TestFlight beta program](https://t.co/xQJmHkfC18).
Build and run If your platform is not listed above, there is still a chance you can manually build it by following the instructions below.
- Using Visual Studio 2017, Rider or Visual Studio Code (configurations are included) ## Downloading the source code
- From command line using `dotnet run --project osu.Desktop`. When building for non-development purposes, add `-c Release` to gain higher performance.
- To run with code analysis, instead use `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
Note: If you run from command line under linux, you will need to prefix the output folder to your `LD_LIBRARY_PATH`. See `.vscode/launch.json` for an example Clone the repository **including submodules**:
If you run into issues building you may need to restore nuget packages (commonly via `dotnet restore`). Visual Studio Code users must run `Restore` task from debug tab before attempt to build. ```shell
git clone --recurse-submodules https://github.com/ppy/osu
cd osu
```
> If you forgot the `--recurse-submodules` option, run this command inside the `osu` directory:
>
> `git submodule update --init --recursive`
To update the source code to the latest commit, run the following command inside the `osu` directory:
```shell
git pull --recurse-submodules
```
## Building
Build configurations for the recommended IDEs (listed above) are included. You should use the provided Build/Run functionality of your IDE to get things going. When testing or building new components, it's highly encouraged you use the `VisualTests` project/configuration. More information on this provided below.
> Visual Studio Code users must run the `Restore` task before any build attempt.
You can also build and run osu! from the command-line with a single command:
```shell
dotnet run --project osu.Desktop
```
If you are not interested in debugging osu!, you can add `-c Release` to gain performance. In this case, you must replace `Debug` with `Release` in any commands mentioned in this document.
If the build fails, try to restore nuget packages with `dotnet restore`.
### A note for Linux users
On Linux, the environment variable `LD_LIBRARY_PATH` must point to the build directory, located at `osu.Desktop/bin/Debug/$NETCORE_VERSION`.
`$NETCORE_VERSION` is the version of .NET Core SDK. You can have it with `grep TargetFramework osu.Desktop/osu.Desktop.csproj | sed -r 's/.*>(.*)<\/.*/\1/'`.
For example, you can run osu! with the following command:
```shell
LD_LIBRARY_PATH="$(pwd)/osu.Desktop/bin/Debug/netcoreapp2.2" dotnet run --project osu.Desktop
```
## Code analysis
Code analysis can be run with `powershell ./build.ps1` or `build.sh`. This is currently only supported under windows due to [resharper cli shortcomings](https://youtrack.jetbrains.com/issue/RSRP-410004). Alternative, you can install resharper or use rider to get inline support in your IDE of choice.
# Contributing # Contributing

View File

@ -97,7 +97,7 @@ namespace osu.Desktop
private void fileDrop(object sender, FileDropEventArgs e) private void fileDrop(object sender, FileDropEventArgs e)
{ {
var filePaths = new[] { e.FileName }; var filePaths = e.FileNames;
var firstExtension = Path.GetExtension(filePaths.First()); var firstExtension = Path.GetExtension(filePaths.First());

View File

@ -28,8 +28,8 @@
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" /> <PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" /> <PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" />
</ItemGroup> </ItemGroup>
<ItemGroup Label="Resources"> <ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" /> <EmbeddedResource Include="lazer.ico" />

View File

@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.Tests
} }
}; };
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 }); beatmap.HitObjects.Add(new Fruit { X = 0.5f + i / 2048f * (i % 10 - 5), StartTime = i * 100, NewCombo = i % 8 == 0 });

View File

@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
{ {
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } }; var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++) for (int i = 0; i < 512; i++)
if (i % 5 < 3) if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 }); beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });

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="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" /> <PackageReference Include="NUnit3TestAdapter" Version="3.12.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

@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{ {
public class CatchDifficultyCalculator : DifficultyCalculator public class CatchDifficultyCalculator : DifficultyCalculator
{ {
/// <summary> /// <summary>
/// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP. /// In milliseconds. For difficulty calculation we will only look at the highest strain value in each time interval of size STRAIN_STEP.
/// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain. /// This is to eliminate higher influence of stream over aim by simply having more HitObjects with high strain.

View File

@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo) if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
} }
private const float preempt = 1000; private const float preempt = 1000;

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="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" /> <PackageReference Include="NUnit3TestAdapter" Version="3.12.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

@ -180,7 +180,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
foreach (var obj in newPattern.HitObjects) foreach (var obj in newPattern.HitObjects)
yield return obj; yield return obj;
} }
} }

View File

@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
if (!calculateStrainValues(difficultyHitObjects, timeRate)) if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0); return new DifficultyAttributes(mods, 0);
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor; double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
return new ManiaDifficultyAttributes(mods, starRating) return new ManiaDifficultyAttributes(mods, starRating)

View File

@ -3,14 +3,14 @@
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Linq; using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModMirror : Mod, IApplicableToRulesetContainer<ManiaHitObject> public class ManiaModMirror : Mod, IApplicableToBeatmap<ManiaHitObject>
{ {
public override string Name => "Mirror"; public override string Name => "Mirror";
public override string Acronym => "MR"; public override string Acronym => "MR";
@ -18,11 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public override bool Ranked => true; public override bool Ranked => true;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{ {
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column); beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = availableColumns - 1 - h.Column);
} }
} }
} }

View File

@ -4,15 +4,15 @@
using System.Linq; using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
namespace osu.Game.Rulesets.Mania.Mods namespace osu.Game.Rulesets.Mania.Mods
{ {
public class ManiaModRandom : Mod, IApplicableToRulesetContainer<ManiaHitObject> public class ManiaModRandom : Mod, IApplicableToBeatmap<ManiaHitObject>
{ {
public override string Name => "Random"; public override string Name => "Random";
public override string Acronym => "RD"; public override string Acronym => "RD";
@ -21,12 +21,12 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Description => @"Shuffle around the keys!"; public override string Description => @"Shuffle around the keys!";
public override double ScoreMultiplier => 1; public override double ScoreMultiplier => 1;
public void ApplyToRulesetContainer(RulesetContainer<ManiaHitObject> rulesetContainer) public void ApplyToBeatmap(Beatmap<ManiaHitObject> beatmap)
{ {
var availableColumns = ((ManiaRulesetContainer)rulesetContainer).Beatmap.TotalColumns; var availableColumns = ((ManiaBeatmap)beatmap).TotalColumns;
var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList(); var shuffledColumns = Enumerable.Range(0, availableColumns).OrderBy(item => RNG.Next()).ToList();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]); beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
} }
} }
} }

View File

@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Mania.Objects
private readonly int beatmapColumnCount; private readonly int beatmapColumnCount;
private readonly double endTime; private readonly double endTime;
private readonly double[] heldUntil; private readonly double[] heldUntil;

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="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" /> <PackageReference Include="NUnit3TestAdapter" Version="3.12.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

@ -57,6 +57,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
s.Process(h); s.Process(h);
} }
// The peak strain will not be saved for the last section in the above loop
foreach (Skill s in skills)
s.SaveCurrentPeak();
double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier; double aimRating = Math.Sqrt(skills[0].DifficultyValue()) * difficulty_multiplier;
double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier; double speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2; double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;

View File

@ -105,13 +105,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double approachRateFactor = 1.0f; double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f) if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.45f * (Attributes.ApproachRate - 10.33f); approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
else if (Attributes.ApproachRate < 8.0f) else if (Attributes.ApproachRate < 8.0f)
{ {
// HD is worth more with lower ar!
if (mods.Any(h => h is OsuModHidden))
approachRateFactor += 0.02f * (8.0f - Attributes.ApproachRate);
else
approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate); approachRateFactor += 0.01f * (8.0f - Attributes.ApproachRate);
} }
@ -119,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
// We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR. // We want to give more reward for lower AR when it comes to aim and HD. This nerfs high AR and buffs lower AR.
if (mods.Any(h => h is OsuModHidden)) if (mods.Any(h => h is OsuModHidden))
aimValue *= 1.02 + (11.0f - Attributes.ApproachRate) / 50.0; // Gives a 1.04 bonus for AR10, a 1.06 bonus for AR9, a 1.02 bonus for AR11. aimValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
if (mods.Any(h => h is OsuModFlashlight)) if (mods.Any(h => h is OsuModFlashlight))
{ {
@ -152,13 +148,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (beatmapMaxCombo > 0) if (beatmapMaxCombo > 0)
speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f); speedValue *= Math.Min(Math.Pow(scoreMaxCombo, 0.8f) / Math.Pow(beatmapMaxCombo, 0.8f), 1.0f);
double approachRateFactor = 1.0f;
if (Attributes.ApproachRate > 10.33f)
approachRateFactor += 0.3f * (Attributes.ApproachRate - 10.33f);
speedValue *= approachRateFactor;
if (mods.Any(m => m is OsuModHidden)) if (mods.Any(m => m is OsuModHidden))
speedValue *= 1.18f; speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
// Scale the speed value with accuracy _slightly_ // Scale the speed value with accuracy _slightly_
speedValue *= 0.5f + accuracy / 2.0f; speedValue *= 0.02f + accuracy;
// It is important to also consider accuracy difficulty when doing that // It is important to also consider accuracy difficulty when doing that
speedValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500; speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
return speedValue; return speedValue;
} }
@ -186,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f)); accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
if (mods.Any(m => m is OsuModHidden)) if (mods.Any(m => m is OsuModHidden))
accuracyValue *= 1.02f; accuracyValue *= 1.08f;
if (mods.Any(m => m is OsuModFlashlight)) if (mods.Any(m => m is OsuModFlashlight))
accuracyValue *= 1.02f; accuracyValue *= 1.02f;

View File

@ -101,8 +101,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty.Preprocessing
float approxFollowCircleRadius = (float)(slider.Radius * 3); float approxFollowCircleRadius = (float)(slider.Radius * 3);
var computeVertex = new Action<double>(t => var computeVertex = new Action<double>(t =>
{ {
double progress = ((int)t - (int)slider.StartTime) / (float)(int)slider.SpanDuration; double progress = (t - slider.StartTime) / slider.SpanDuration;
if (progress % 2 > 1) if (progress % 2 >= 1)
progress = 1 - progress % 1; progress = 1 - progress % 1;
else else
progress = progress % 1; progress = progress % 1;

View File

@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.SkinChanged(skin, allowFallback); base.SkinChanged(skin, allowFallback);
if (HitObject is IHasComboInformation combo) if (HitObject is IHasComboInformation combo)
AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : (Color4?)null) ?? Color4.White; AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.ComboColours.Count > 0 ? s.ComboColours[combo.ComboIndex % s.ComboColours.Count] : Color4.White);
} }
protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn); protected virtual void UpdatePreemptState() => this.FadeIn(HitObject.TimeFadeIn);

View File

@ -101,7 +101,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break; break;
} }
float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X)); float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180) while (Math.Abs(aimRotation - Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360; aimRotation += aimRotation < Rotation ? 360 : -360;

View File

@ -14,6 +14,7 @@ using osu.Game.Configuration;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osuTK.Graphics; using osuTK.Graphics;
using osu.Game.Skinning;
namespace osu.Game.Rulesets.Osu.Objects.Drawables namespace osu.Game.Rulesets.Osu.Objects.Drawables
{ {
@ -151,6 +152,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
} }
} }
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
base.SkinChanged(skin, allowFallback);
Body.AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderTrackOverride") ? s.CustomColours["SliderTrackOverride"] : Body.AccentColour);
Body.BorderColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderBorder") ? s.CustomColours["SliderBorder"] : Body.BorderColour);
Ball.AccentColour = skin.GetValue<SkinConfiguration, Color4>(s => s.CustomColours.ContainsKey("SliderBall") ? s.CustomColours["SliderBall"] : Ball.AccentColour);
}
protected override void CheckForResult(bool userTriggered, double timeOffset) protected override void CheckForResult(bool userTriggered, double timeOffset)
{ {
if (userTriggered || Time.Current < slider.EndTime) if (userTriggered || Time.Current < slider.EndTime)

View File

@ -198,7 +198,7 @@ namespace osu.Game.Rulesets.Osu.Objects
if (tickDistance == 0) return; if (tickDistance == 0) return;
var minDistanceFromEnd = Velocity * 0.01; var minDistanceFromEnd = Velocity * 10;
var spanCount = this.SpanCount(); var spanCount = this.SpanCount();

View File

@ -39,10 +39,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private int downCount; private int downCount;
private const float pressed_scale = 1.2f; private void updateExpandedState()
private const float released_scale = 1f; {
if (downCount > 0)
private float targetScale => downCount > 0 ? pressed_scale : released_scale; (ActiveCursor as OsuCursor)?.Expand();
else
(ActiveCursor as OsuCursor)?.Contract();
}
public bool OnPressed(OsuAction action) public bool OnPressed(OsuAction action)
{ {
@ -51,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
downCount++; downCount++;
ActiveCursor.ScaleTo(released_scale).ScaleTo(targetScale, 100, Easing.OutQuad); updateExpandedState();
break; break;
} }
@ -65,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton: case OsuAction.LeftButton:
case OsuAction.RightButton: case OsuAction.RightButton:
if (--downCount == 0) if (--downCount == 0)
ActiveCursor.ScaleTo(targetScale, 200, Easing.OutQuad); updateExpandedState();
break; break;
} }
@ -77,92 +80,106 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn() protected override void PopIn()
{ {
fadeContainer.FadeTo(1, 300, Easing.OutQuint); fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale, 400, Easing.OutQuint); ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
} }
protected override void PopOut() protected override void PopOut()
{ {
fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint); fadeContainer.FadeTo(0.05f, 450, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale * 0.8f, 450, Easing.OutQuint); ActiveCursor.ScaleTo(0.8f, 450, Easing.OutQuint);
} }
public class OsuCursor : Container public class OsuCursor : SkinReloadableDrawable
{ {
private Drawable cursorContainer; private bool cursorExpand;
private Bindable<double> cursorScale; private Bindable<double> cursorScale;
private Bindable<bool> autoCursorScale; private Bindable<bool> autoCursorScale;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>(); private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private Container expandTarget;
private Drawable scaleTarget;
public OsuCursor() public OsuCursor()
{ {
Origin = Anchor.Centre; Origin = Anchor.Centre;
Size = new Vector2(42); Size = new Vector2(42);
} }
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindableBeatmap beatmap) private void load(OsuConfigManager config, IBindableBeatmap beatmap)
{ {
Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer InternalChild = expandTarget = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both, Child = scaleTarget = new SkinnableDrawable("cursor", _ => new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 6,
BorderColour = Color4.White,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Shadow,
Colour = Color4.Pink.Opacity(0.5f),
Radius = 5,
},
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = Size.X / 3,
BorderColour = Color4.White.Opacity(0.5f),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
},
},
},
new CircularContainer
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Scale = new Vector2(0.1f),
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
},
},
}
}, restrictSize: false)
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
}
}; };
this.beatmap.BindTo(beatmap); this.beatmap.BindTo(beatmap);
@ -187,8 +204,19 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY); scale *= (float)(1 - 0.7 * (1 + beatmap.Value.BeatmapInfo.BaseDifficulty.CircleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY);
} }
cursorContainer.Scale = new Vector2(scale); scaleTarget.Scale = new Vector2(scale);
} }
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
public void Expand()
{
if (!cursorExpand) return;
expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 100, Easing.OutQuad);
}
public void Contract() => expandTarget.ScaleTo(released_scale, 100, Easing.OutQuad);
} }
} }
} }

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="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" /> <PackageReference Include="NUnit3TestAdapter" Version="3.12.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

@ -149,7 +149,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = Resource.OpenResource(filename)) using (var stream = Resource.OpenResource(filename))
using (var sr = new StreamReader(stream)) using (var sr = new StreamReader(stream))
{ {
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr); var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
using (var ms = new MemoryStream()) using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms)) using (var sw = new StreamWriter(ms))

View File

@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.ComponentModel; using System.ComponentModel;
using System.Linq; using System.Linq;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -23,11 +24,17 @@ namespace osu.Game.Tests.Visual
}; };
} }
protected override bool ContinueCondition(Player player) => base.ContinueCondition(player) && ((ScoreAccessiblePlayer)player).ScoreProcessor.TotalScore > 0; protected override void AddCheckSteps(Func<Player> player)
{
base.AddCheckSteps(player);
AddUntilStep(() => ((ScoreAccessiblePlayer)player()).ScoreProcessor.TotalScore > 0, "score above zero");
AddUntilStep(() => ((ScoreAccessiblePlayer)player()).HUDOverlay.KeyCounter.Children.Any(kc => kc.CountPresses > 0), "key counter counted keys");
}
private class ScoreAccessiblePlayer : Player private class ScoreAccessiblePlayer : Player
{ {
public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; public new ScoreProcessor ScoreProcessor => base.ScoreProcessor;
public new HUDOverlay HUDOverlay => base.HUDOverlay;
} }
} }
} }

View File

@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual
typeof(DrawableCarouselBeatmapSet), typeof(DrawableCarouselBeatmapSet),
}; };
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>(); private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>(); private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>();

View File

@ -55,7 +55,6 @@ namespace osu.Game.Tests.Visual
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300)); AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo }); AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[] scores = new[]
{ {
new APIScoreInfo new APIScoreInfo

View File

@ -16,7 +16,6 @@ namespace osu.Game.Tests.Visual
public TestCaseBreadcrumbs() public TestCaseBreadcrumbs()
{ {
Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab> Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab>
{ {
Anchor = Anchor.Centre, Anchor = Anchor.Centre,

View File

@ -39,7 +39,6 @@ namespace osu.Game.Tests.Visual
}, },
}; };
AddStep("Add random", () => AddStep("Add random", () =>
{ {
Key key = (Key)((int)Key.A + RNG.Next(26)); Key key = (Key)((int)Key.A + RNG.Next(26));

View File

@ -17,12 +17,13 @@ namespace osu.Game.Tests.Visual
{ {
public TestCaseMatchLeaderboard() public TestCaseMatchLeaderboard()
{ {
Add(new MatchLeaderboard(new Room { RoomID = { Value = 3 } }) Add(new MatchLeaderboard
{ {
Origin = Anchor.Centre, Origin = Anchor.Centre,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f), Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall, Scope = MatchLeaderboardScope.Overall,
Room = new Room { RoomID = { Value = 3 } }
}); });
} }

View File

@ -57,11 +57,19 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect private class TestSongSelect : PlaySongSelect
{ {
public Action StartRequested;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset; public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
public WorkingBeatmap CurrentBeatmap => Beatmap.Value; public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap; public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel; public new BeatmapCarousel Carousel => base.Carousel;
protected override bool OnStart()
{
StartRequested?.Invoke();
return base.OnStart();
}
} }
private TestSongSelect songSelect; private TestSongSelect songSelect;
@ -182,6 +190,27 @@ namespace osu.Game.Tests.Visual
void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--; void onRulesetChange(RulesetInfo ruleset) => rulesetChangeIndex = actionIndex--;
} }
[Test]
public void TestStartAfterUnMatchingFilterDoesNotStart()
{
addManyTestMaps();
AddUntilStep(() => songSelect.Carousel.SelectedBeatmap != null, "has selection");
bool startRequested = false;
AddStep("set filter and finalize", () =>
{
songSelect.StartRequested = () => startRequested = true;
songSelect.Carousel.Filter(new FilterCriteria { SearchText = "somestringthatshouldn'tbematchable" });
songSelect.FinaliseSelection();
songSelect.StartRequested = null;
});
AddAssert("start not requested", () => !startRequested);
}
private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray()))); private void importForRuleset(int id) => AddStep($"import test map for ruleset {id}", () => manager.Import(createTestBeatmapSet(getImportId(), rulesets.AvailableRulesets.Where(r => r.ID == id).ToArray())));
private static int importId; private static int importId;

View File

@ -57,5 +57,4 @@ namespace osu.Game.Tests.Visual
} }
} }
} }
} }

View File

@ -7,7 +7,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
[TestFixture] [TestFixture]
public class TestCaseSkipButton : OsuTestCase public class TestCaseSkipOverlay : OsuTestCase
{ {
protected override void LoadComplete() protected override void LoadComplete()
{ {

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="15.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="NUnit3TestAdapter" Version="3.11.2" /> <PackageReference Include="NUnit3TestAdapter" Version="3.12.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

@ -17,7 +17,6 @@ using osu.Framework.Logging;
using osu.Framework.Platform; using osu.Framework.Platform;
using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Formats;
using osu.Game.Database; using osu.Game.Database;
using osu.Game.Graphics;
using osu.Game.IO.Archives; using osu.Game.IO.Archives;
using osu.Game.Online.API; using osu.Game.Online.API;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
@ -147,16 +146,6 @@ namespace osu.Game.Beatmaps
if (existing != null || api == null) return false; if (existing != null || api == null) return false;
if (!api.LocalUser.Value.IsSupporter)
{
PostNotification?.Invoke(new SimpleNotification
{
Icon = FontAwesome.fa_superpowers,
Text = "You gotta be an osu!supporter to download for now 'yo"
});
return false;
}
var downloadNotification = new DownloadNotification var downloadNotification = new DownloadNotification
{ {
CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!", CompletionText = $"Imported {beatmapSetInfo.Metadata.Artist} - {beatmapSetInfo.Metadata.Title}!",

View File

@ -105,6 +105,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f); Set(OsuSetting.ScalingPositionX, 0.5f, 0f, 1f);
Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f); Set(OsuSetting.ScalingPositionY, 0.5f, 0f, 1f);
Set(OsuSetting.UIScale, 1f, 0.8f, 1.6f, 0.01f);
} }
public OsuConfigManager(Storage storage) public OsuConfigManager(Storage storage)
@ -167,6 +169,7 @@ namespace osu.Game.Configuration
ScalingPositionX, ScalingPositionX,
ScalingPositionY, ScalingPositionY,
ScalingSizeX, ScalingSizeX,
ScalingSizeY ScalingSizeY,
UIScale
} }
} }

View File

@ -61,24 +61,25 @@ namespace osu.Game.Graphics.Containers
AddText(text.Substring(previousLinkEnd)); AddText(text.Substring(previousLinkEnd));
} }
public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action<SpriteText> creationParameters = null) public IEnumerable<Drawable> AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), text, url, linkType, linkArgument, tooltipText); => createLink(AddText(text, creationParameters), text, url, linkType, linkArgument, tooltipText);
public void AddLink(string text, Action action, string tooltipText = null, Action<SpriteText> creationParameters = null) public IEnumerable<Drawable> AddLink(string text, Action action, string tooltipText = null, Action<SpriteText> creationParameters = null)
=> createLink(AddText(text, creationParameters), text, tooltipText: tooltipText, action: action); => createLink(AddText(text, creationParameters), text, tooltipText: tooltipText, action: action);
public void AddLink(IEnumerable<SpriteText> text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null) public IEnumerable<Drawable> AddLink(IEnumerable<SpriteText> text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null)
{ {
foreach (var t in text) foreach (var t in text)
AddArbitraryDrawable(t); AddArbitraryDrawable(t);
createLink(text, null, url, linkType, linkArgument, tooltipText); return createLink(text, null, url, linkType, linkArgument, tooltipText);
} }
private void createLink(IEnumerable<Drawable> drawables, string text, string url = null, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action action = null) private IEnumerable<Drawable> createLink(IEnumerable<Drawable> drawables, string text, string url = null, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action action = null)
{ {
AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList()) AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList())
{ {
RelativeSizeAxes = Axes.Both,
TooltipText = tooltipText ?? (url != text ? url : string.Empty), TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = action ?? (() => Action = action ?? (() =>
{ {
@ -121,6 +122,13 @@ namespace osu.Game.Graphics.Containers
} }
}), }),
}); });
return drawables;
} }
// We want the compilers to always be visible no matter where they are, so RelativeSizeAxes is used.
// However due to https://github.com/ppy/osu-framework/issues/2073, it's possible for the compilers to be relative size in the flow's auto-size axes - an unsupported operation.
// Since the compilers don't display any content and don't affect the layout, it's simplest to exclude them from the flow.
public override IEnumerable<Drawable> FlowingChildren => base.FlowingChildren.Where(c => !(c is DrawableLinkCompiler));
} }
} }

View File

@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Sprites;
@ -19,6 +20,6 @@ namespace osu.Game.Graphics.Containers
public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable); public void AddArbitraryDrawable(Drawable drawable) => AddInternal(drawable);
public void AddIcon(FontAwesome icon, Action<SpriteText> creationParameters = null) => AddText(((char)icon).ToString(), creationParameters); public IEnumerable<Drawable> AddIcon(FontAwesome icon, Action<SpriteText> creationParameters = null) => AddText(((char)icon).ToString(), creationParameters);
} }
} }

View File

@ -28,6 +28,8 @@ namespace osu.Game.Graphics.Containers
private readonly Container content; private readonly Container content;
protected override Container<Drawable> Content => content; protected override Container<Drawable> Content => content;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private readonly Container sizableContainer; private readonly Container sizableContainer;
private Drawable backgroundLayer; private Drawable backgroundLayer;
@ -41,15 +43,44 @@ namespace osu.Game.Graphics.Containers
this.targetMode = targetMode; this.targetMode = targetMode;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
InternalChild = sizableContainer = new Container InternalChild = sizableContainer = new AlwaysInputContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both, RelativePositionAxes = Axes.Both,
CornerRadius = 10, CornerRadius = 10,
Child = content = new DrawSizePreservingFillContainer() Child = content = new ScalingDrawSizePreservingFillContainer(targetMode != ScalingMode.Gameplay)
}; };
} }
private class ScalingDrawSizePreservingFillContainer : DrawSizePreservingFillContainer
{
private readonly bool applyUIScale;
private Bindable<float> uiScale;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public ScalingDrawSizePreservingFillContainer(bool applyUIScale)
{
this.applyUIScale = applyUIScale;
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager osuConfig)
{
if (applyUIScale)
{
uiScale = osuConfig.GetBindable<float>(OsuSetting.UIScale);
uiScale.BindValueChanged(scaleChanged, true);
}
}
private void scaleChanged(float value)
{
this.ScaleTo(new Vector2(value), 500, Easing.Out);
this.ResizeTo(new Vector2(1 / value), 500, Easing.Out);
}
}
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
@ -116,5 +147,15 @@ namespace osu.Game.Graphics.Containers
sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart); sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart);
sizableContainer.ResizeTo(targetSize, 500, Easing.OutQuart).OnComplete(_ => { sizableContainer.Masking = requiresMasking; }); sizableContainer.ResizeTo(targetSize, 500, Easing.OutQuart).OnComplete(_ => { sizableContainer.Masking = requiresMasking; });
} }
private class AlwaysInputContainer : Container
{
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
public AlwaysInputContainer()
{
RelativeSizeAxes = Axes.Both;
}
}
} }
} }

View File

@ -92,6 +92,12 @@ namespace osu.Game.Graphics.UserInterface
AccentColour = colours.Pink; AccentColour = colours.Pink;
} }
protected override void LoadComplete()
{
base.LoadComplete();
CurrentNumber.BindValueChanged(updateTooltipText, true);
}
protected override bool OnHover(HoverEvent e) protected override bool OnHover(HoverEvent e)
{ {
Nub.Glowing = true; Nub.Glowing = true;

View File

@ -160,7 +160,6 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetDescription() ?? value.ToString(), Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14, TextSize = 14,
Font = @"Exo2.0-Bold", // Font should only turn bold when active?
}, },
Bar = new Box Bar = new Box
{ {
@ -173,6 +172,8 @@ namespace osu.Game.Graphics.UserInterface
}, },
new HoverClickSounds() new HoverClickSounds()
}; };
Active.BindValueChanged(val => Text.Font = val ? @"Exo2.0-Bold" : @"Exo2.0", true);
} }
protected override void OnActivated() => fadeActive(); protected override void OnActivated() => fadeActive();

View File

@ -46,7 +46,6 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Text = (value as Enum)?.GetDescription() ?? value.ToString(), Text = (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14, TextSize = 14,
Font = @"Exo2.0-Bold",
}, },
box = new Box box = new Box
{ {
@ -59,6 +58,8 @@ namespace osu.Game.Graphics.UserInterface
}, },
new HoverClickSounds() new HoverClickSounds()
}; };
Active.BindValueChanged(val => Text.Font = val ? @"Exo2.0-Bold" : @"Exo2.0", true);
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]

View File

@ -101,6 +101,9 @@ namespace osu.Game.Online.API
//todo: replace this with a ping request. //todo: replace this with a ping request.
log.Add(@"In a failing state, waiting a bit before we try again..."); log.Add(@"In a failing state, waiting a bit before we try again...");
Thread.Sleep(5000); Thread.Sleep(5000);
if (!IsLoggedIn) goto case APIState.Connecting;
if (queue.Count == 0) if (queue.Count == 0)
{ {
log.Add(@"Queueing a ping request"); log.Add(@"Queueing a ping request");

View File

@ -41,7 +41,6 @@ namespace osu.Game.Online.Chat
/// </summary> /// </summary>
private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>(); private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>();
/// <summary> /// <summary>
/// An event that fires when new messages arrived. /// An event that fires when new messages arrived.
/// </summary> /// </summary>

View File

@ -221,13 +221,18 @@ namespace osu.Game
return; return;
} }
var databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID); var databasedSet = beatmap.OnlineBeatmapSetID != null ?
BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID) :
BeatmapManager.QueryBeatmapSet(s => s.Hash == beatmap.Hash);
// Use first beatmap available for current ruleset, else switch ruleset. if (databasedSet != null)
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First(); {
// Use first beatmap available for current ruleset, else switch ruleset.
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
ruleset.Value = first.Ruleset; ruleset.Value = first.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
}
} }
switch (currentScreen) switch (currentScreen)
@ -359,8 +364,11 @@ namespace osu.Game
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
mainContent = new DrawSizePreservingFillContainer(), overlayContent = new Container
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue }, {
RelativeSizeAxes = Axes.Both,
},
floatingOverlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
idleTracker = new IdleTracker(6000) idleTracker = new IdleTracker(6000)
}); });
@ -379,32 +387,32 @@ namespace osu.Game
CloseAllOverlays(false); CloseAllOverlays(false);
intro?.ChildScreen?.MakeCurrent(); intro?.ChildScreen?.MakeCurrent();
}, },
}, overlayContent.Add); }, floatingOverlayContent.Add);
loadComponentSingleFile(volume = new VolumeOverlay(), overlayContent.Add); loadComponentSingleFile(volume = new VolumeOverlay(), floatingOverlayContent.Add);
loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add); loadComponentSingleFile(onscreenDisplay = new OnScreenDisplay(), Add);
loadComponentSingleFile(screenshotManager, Add); loadComponentSingleFile(screenshotManager, Add);
//overlay elements //overlay elements
loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(direct = new DirectOverlay { Depth = -1 }, overlayContent.Add);
loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(social = new SocialOverlay { Depth = -1 }, overlayContent.Add);
loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal);
loadComponentSingleFile(chatOverlay = new ChatOverlay { Depth = -1 }, mainContent.Add); loadComponentSingleFile(chatOverlay = new ChatOverlay { Depth = -1 }, overlayContent.Add);
loadComponentSingleFile(settings = new MainSettings loadComponentSingleFile(settings = new MainSettings
{ {
GetToolbarHeight = () => ToolbarOffset, GetToolbarHeight = () => ToolbarOffset,
Depth = -1 Depth = -1
}, overlayContent.Add); }, floatingOverlayContent.Add);
loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, mainContent.Add); loadComponentSingleFile(userProfile = new UserProfileOverlay { Depth = -2 }, overlayContent.Add);
loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, mainContent.Add); loadComponentSingleFile(beatmapSetOverlay = new BeatmapSetOverlay { Depth = -3 }, overlayContent.Add);
loadComponentSingleFile(musicController = new MusicController loadComponentSingleFile(musicController = new MusicController
{ {
Depth = -5, Depth = -5,
Position = new Vector2(0, Toolbar.HEIGHT), Position = new Vector2(0, Toolbar.HEIGHT),
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
}, overlayContent.Add); }, floatingOverlayContent.Add);
loadComponentSingleFile(notifications = new NotificationOverlay loadComponentSingleFile(notifications = new NotificationOverlay
{ {
@ -412,22 +420,22 @@ namespace osu.Game
Depth = -4, Depth = -4,
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
}, overlayContent.Add); }, floatingOverlayContent.Add);
loadComponentSingleFile(accountCreation = new AccountCreationOverlay loadComponentSingleFile(accountCreation = new AccountCreationOverlay
{ {
Depth = -6, Depth = -6,
}, overlayContent.Add); }, floatingOverlayContent.Add);
loadComponentSingleFile(dialogOverlay = new DialogOverlay loadComponentSingleFile(dialogOverlay = new DialogOverlay
{ {
Depth = -7, Depth = -7,
}, overlayContent.Add); }, floatingOverlayContent.Add);
loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener
{ {
Depth = -8, Depth = -8,
}, overlayContent.Add); }, floatingOverlayContent.Add);
dependencies.Cache(idleTracker); dependencies.Cache(idleTracker);
dependencies.Cache(settings); dependencies.Cache(settings);
@ -646,10 +654,10 @@ namespace osu.Game
public bool OnReleased(GlobalAction action) => false; public bool OnReleased(GlobalAction action) => false;
private Container mainContent;
private Container overlayContent; private Container overlayContent;
private Container floatingOverlayContent;
private OsuScreen currentScreen; private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig; private FrameworkConfigManager frameworkConfig;
private ScalingContainer screenContainer; private ScalingContainer screenContainer;
@ -693,6 +701,7 @@ namespace osu.Game
Beatmap.Disabled = applyBeatmapRulesetRestrictions; Beatmap.Disabled = applyBeatmapRulesetRestrictions;
screenContainer.Padding = new MarginPadding { Top = ToolbarOffset }; screenContainer.Padding = new MarginPadding { Top = ToolbarOffset };
overlayContent.Padding = new MarginPadding { Top = ToolbarOffset };
MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false; MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class DownloadButton : HeaderButton, IHasTooltip public class DownloadButton : HeaderButton, IHasTooltip
{ {
public string TooltipText => Enabled ? null : "You gotta be an osu!supporter to download for now 'yo"; public string TooltipText => "Download this beatmap";
private readonly IBindable<User> localUser = new Bindable<User>(); private readonly IBindable<User> localUser = new Bindable<User>();
@ -101,12 +101,9 @@ namespace osu.Game.Overlays.BeatmapSet.Buttons
private void load(APIAccess api) private void load(APIAccess api)
{ {
localUser.BindTo(api.LocalUser); localUser.BindTo(api.LocalUser);
localUser.BindValueChanged(userChanged, true);
Enabled.BindValueChanged(enabledChanged, true); Enabled.BindValueChanged(enabledChanged, true);
} }
private void userChanged(User user) => Enabled.Value = user.IsSupporter;
private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint); private void enabledChanged(bool enabled) => this.FadeColour(enabled ? Color4.White : Color4.Gray, 200, Easing.OutQuint);
} }
} }

View File

@ -77,7 +77,6 @@ namespace osu.Game.Overlays.Chat.Tabs
CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint); CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
} }
protected override void FadeInactive() protected override void FadeInactive()
{ {
base.FadeInactive(); base.FadeInactive();

View File

@ -18,7 +18,6 @@ namespace osu.Game.Overlays.KeyBinding
Add(new InGameKeyBindingsSubsection(manager)); Add(new InGameKeyBindingsSubsection(manager));
} }
private class DefaultBindingsSubsection : KeyBindingsSubsection private class DefaultBindingsSubsection : KeyBindingsSubsection
{ {
protected override string Header => string.Empty; protected override string Header => string.Empty;

View File

@ -176,7 +176,6 @@ namespace osu.Game.Overlays.Mods
section.DeselectTypes(modTypes, immediate); section.DeselectTypes(modTypes, immediate);
} }
private SampleChannel sampleOn, sampleOff; private SampleChannel sampleOn, sampleOff;
private void modButtonPressed(Mod selectedMod) private void modButtonPressed(Mod selectedMod)

View File

@ -49,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
Api.Queue(request); Api.Queue(request);
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)
{ {
base.Dispose(isDisposing); base.Dispose(isDisposing);

View File

@ -63,9 +63,16 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y AutoSizeAxes = Axes.Y
}, },
new SettingsSlider<float, UIScaleSlider>
{
LabelText = "UI Scaling",
TransferValueOnCommit = true,
Bindable = osuConfig.GetBindable<float>(OsuSetting.UIScale),
KeyboardStep = 0.01f
},
new SettingsEnumDropdown<ScalingMode> new SettingsEnumDropdown<ScalingMode>
{ {
LabelText = "Scaling", LabelText = "Screen Scaling",
Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling), Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
}, },
scalingSettings = new FillFlowContainer<SettingsSlider<float>> scalingSettings = new FillFlowContainer<SettingsSlider<float>>
@ -202,6 +209,11 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
} }
} }
private class UIScaleSlider : OsuSliderBar<float>
{
public override string TooltipText => base.TooltipText + "x";
}
private class ResolutionSettingsDropdown : SettingsDropdown<Size> private class ResolutionSettingsDropdown : SettingsDropdown<Size>
{ {
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items }; protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items };

View File

@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Input; using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
@ -78,66 +77,15 @@ namespace osu.Game.Overlays.Settings.Sections.Input
private class SensitivitySetting : SettingsSlider<double, SensitivitySlider> private class SensitivitySetting : SettingsSlider<double, SensitivitySlider>
{ {
public override Bindable<double> Bindable
{
get { return ((SensitivitySlider)Control).Sensitivity; }
set
{
BindableDouble doubleValue = (BindableDouble)value;
// create a second layer of bindable so we can only handle state changes when not being dragged.
((SensitivitySlider)Control).Sensitivity = doubleValue;
// this bindable will still act as the "interactive" bindable displayed during a drag.
base.Bindable = new BindableDouble(doubleValue.Value)
{
Default = doubleValue.Default,
MinValue = doubleValue.MinValue,
MaxValue = doubleValue.MaxValue
};
// one-way binding to update the sliderbar with changes from external actions.
doubleValue.DisabledChanged += disabled => base.Bindable.Disabled = disabled;
doubleValue.ValueChanged += newValue => base.Bindable.Value = newValue;
}
}
public SensitivitySetting() public SensitivitySetting()
{ {
KeyboardStep = 0.01f; KeyboardStep = 0.01f;
TransferValueOnCommit = true;
} }
} }
private class SensitivitySlider : OsuSliderBar<double> private class SensitivitySlider : OsuSliderBar<double>
{ {
public Bindable<double> Sensitivity;
public SensitivitySlider()
{
Current.ValueChanged += newValue =>
{
if (!isDragging && Sensitivity != null)
Sensitivity.Value = newValue;
};
}
private bool isDragging;
protected override bool OnDragStart(DragStartEvent e)
{
isDragging = true;
return base.OnDragStart(e);
}
protected override bool OnDragEnd(DragEndEvent e)
{
isDragging = false;
Current.TriggerChange();
return base.OnDragEnd(e);
}
public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x"); public override string TooltipText => Current.Disabled ? "Enable raw input to adjust sensitivity" : Current.Value.ToString(@"0.##x");
} }
} }

View File

@ -52,6 +52,16 @@ namespace osu.Game.Overlays.Settings.Sections
LabelText = "Adjust gameplay cursor size based on current beatmap", LabelText = "Adjust gameplay cursor size based on current beatmap",
Bindable = config.GetBindable<bool>(OsuSetting.AutoCursorSize) Bindable = config.GetBindable<bool>(OsuSetting.AutoCursorSize)
}, },
new SettingsCheckbox
{
LabelText = "Beatmap skins",
Bindable = config.GetBindable<bool>(OsuSetting.BeatmapSkins)
},
new SettingsCheckbox
{
LabelText = "Beatmap hitsounds",
Bindable = config.GetBindable<bool>(OsuSetting.BeatmapHitsounds)
},
}; };
skins.ItemAdded += itemAdded; skins.ItemAdded += itemAdded;

View File

@ -26,6 +26,8 @@ namespace osu.Game.Overlays.Settings
} }
} }
public override IEnumerable<string> FilterTerms => base.FilterTerms.Concat(Control.Items.Select(i => i.ToString()));
protected sealed override Drawable CreateControl() => CreateDropdown(); protected sealed override Drawable CreateControl() => CreateDropdown();
protected virtual OsuDropdown<T> CreateDropdown() => new DropdownControl { Items = Items }; protected virtual OsuDropdown<T> CreateDropdown() => new DropdownControl { Items = Items };

View File

@ -72,7 +72,7 @@ namespace osu.Game.Overlays.Settings
} }
} }
public IEnumerable<string> FilterTerms => new[] { LabelText }; public virtual IEnumerable<string> FilterTerms => new[] { LabelText };
public bool MatchingFilter public bool MatchingFilter
{ {

View File

@ -124,7 +124,6 @@ namespace osu.Game.Rulesets.Objects.Legacy
// osu-stable treated the first span of the slider as a repeat, but no repeats are happening // osu-stable treated the first span of the slider as a repeat, but no repeats are happening
repeatCount = Math.Max(0, repeatCount - 1); repeatCount = Math.Max(0, repeatCount - 1);
if (split.Length > 7) if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);

View File

@ -250,14 +250,14 @@ namespace osu.Game.Rulesets.UI
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
KeyBindingInputManager.Children = new Drawable[] KeyBindingInputManager.AddRange(new Drawable[]
{ {
content = new Container content = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}, },
Playfield Playfield
}; });
if (Cursor != null) if (Cursor != null)
KeyBindingInputManager.Add(Cursor); KeyBindingInputManager.Add(Cursor);

View File

@ -27,14 +27,6 @@ namespace osu.Game.Rulesets.UI
public abstract class RulesetInputManager<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler public abstract class RulesetInputManager<T> : PassThroughInputManager, ICanAttachKeyCounter, IHasReplayHandler
where T : struct where T : struct
{ {
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
{
public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
: base(ruleset, variant, unique)
{
}
}
protected override InputState CreateInitialState() protected override InputState CreateInitialState()
{ {
var state = base.CreateInitialState(); var state = base.CreateInitialState();
@ -251,6 +243,14 @@ namespace osu.Game.Rulesets.UI
protected virtual RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique) protected virtual RulesetKeyBindingContainer CreateKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
=> new RulesetKeyBindingContainer(ruleset, variant, unique); => new RulesetKeyBindingContainer(ruleset, variant, unique);
public class RulesetKeyBindingContainer : DatabasedKeyBindingContainer<T>
{
public RulesetKeyBindingContainer(RulesetInfo ruleset, int variant, SimultaneousBindingMode unique)
: base(ruleset, variant, unique)
{
}
}
} }
/// <summary> /// <summary>

View File

@ -109,12 +109,12 @@ namespace osu.Game.Scoring.Legacy
} }
} }
calculateAccuracy(score.ScoreInfo); CalculateAccuracy(score.ScoreInfo);
return score; return score;
} }
private void calculateAccuracy(ScoreInfo score) protected void CalculateAccuracy(ScoreInfo score)
{ {
int countMiss = score.Statistics[HitResult.Miss]; int countMiss = score.Statistics[HitResult.Miss];
int count50 = score.Statistics[HitResult.Meh]; int count50 = score.Statistics[HitResult.Meh];

View File

@ -2,10 +2,14 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.MathUtils; using osu.Framework.MathUtils;
using osu.Framework.Threading; using osu.Framework.Threading;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using osu.Game.Online.API;
using osu.Game.Skinning;
using osu.Game.Users;
namespace osu.Game.Screens.Backgrounds namespace osu.Game.Screens.Backgrounds
{ {
@ -16,11 +20,21 @@ namespace osu.Game.Screens.Backgrounds
private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}"; private string backgroundName => $@"Menu/menu-background-{currentDisplay % background_count + 1}";
private Bindable<User> user;
private Bindable<Skin> skin;
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load(IAPIProvider api, SkinManager skinManager)
{ {
user = api.LocalUser.GetBoundCopy();
skin = skinManager.CurrentSkin.GetBoundCopy();
user.ValueChanged += _ => Next();
skin.ValueChanged += _ => Next();
currentDisplay = RNG.Next(0, background_count); currentDisplay = RNG.Next(0, background_count);
display(new Background(backgroundName));
Next();
} }
private void display(Background newBackground) private void display(Background newBackground)
@ -39,8 +53,33 @@ namespace osu.Game.Screens.Backgrounds
nextTask?.Cancel(); nextTask?.Cancel();
nextTask = Scheduler.AddDelayed(() => nextTask = Scheduler.AddDelayed(() =>
{ {
LoadComponentAsync(new Background(backgroundName) { Depth = currentDisplay }, display); Background background;
if (user.Value?.IsSupporter ?? false)
background = new SkinnedBackground(skin.Value, backgroundName);
else
background = new Background(backgroundName);
background.Depth = currentDisplay;
LoadComponentAsync(background, display);
}, 100); }, 100);
} }
private class SkinnedBackground : Background
{
private readonly Skin skin;
public SkinnedBackground(Skin skin, string fallbackTextureName) : base(fallbackTextureName)
{
this.skin = skin;
}
[BackgroundDependencyLoader]
private void load()
{
Sprite.Texture = skin.GetTexture("menu-background") ?? Sprite.Texture;
}
}
} }
} }

View File

@ -17,7 +17,6 @@ namespace osu.Game.Screens.Edit.Components
public TimeInfoContainer() public TimeInfoContainer()
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
trackTimer = new OsuSpriteText trackTimer = new OsuSpriteText

View File

@ -1,8 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
@ -24,7 +28,10 @@ namespace osu.Game.Screens.Menu
public override bool CursorVisible => false; public override bool CursorVisible => false;
private const float icon_y = -0.09f; private readonly List<Drawable> supporterDrawables = new List<Drawable>();
private Drawable heart;
private const float icon_y = -85;
public Disclaimer() public Disclaimer()
{ {
@ -42,7 +49,6 @@ namespace osu.Game.Screens.Menu
Origin = Anchor.Centre, Origin = Anchor.Centre,
Icon = FontAwesome.fa_warning, Icon = FontAwesome.fa_warning,
Size = new Vector2(30), Size = new Vector2(30),
RelativePositionAxes = Axes.Both,
Y = icon_y, Y = icon_y,
}, },
textFlow = new LinkFlowContainer textFlow = new LinkFlowContainer
@ -51,8 +57,9 @@ namespace osu.Game.Screens.Menu
AutoSizeAxes = Axes.Y, AutoSizeAxes = Axes.Y,
Padding = new MarginPadding(50), Padding = new MarginPadding(50),
TextAnchor = Anchor.TopCentre, TextAnchor = Anchor.TopCentre,
Y = -110,
Anchor = Anchor.Centre, Anchor = Anchor.Centre,
Origin = Anchor.Centre, Origin = Anchor.TopCentre,
Spacing = new Vector2(0, 2), Spacing = new Vector2(0, 2),
} }
}; };
@ -68,14 +75,37 @@ namespace osu.Game.Screens.Menu
t.Font = @"Exo2.0-SemiBold"; t.Font = @"Exo2.0-SemiBold";
}); });
textFlow.AddParagraph("Don't expect everything to work perfectly."); textFlow.AddParagraph("Things may not work as expected", t => t.TextSize = 20);
textFlow.AddParagraph(""); textFlow.NewParagraph();
textFlow.AddParagraph("Detailed bug reports are welcomed via github issues.");
textFlow.AddParagraph("");
textFlow.AddText("Visit "); Action<SpriteText> format = t =>
textFlow.AddLink("discord.gg/ppy", "https://discord.gg/ppy"); {
textFlow.AddText(" if you want to help out or follow progress!"); t.TextSize = 15;
t.Font = @"Exo2.0-SemiBold";
};
textFlow.AddParagraph("Detailed bug reports are welcomed via github issues.", format);
textFlow.NewParagraph();
textFlow.AddText("Visit ", format);
textFlow.AddLink("discord.gg/ppy", "https://discord.gg/ppy", creationParameters:format);
textFlow.AddText(" to help out or follow progress!", format);
textFlow.NewParagraph();
textFlow.NewParagraph();
textFlow.NewParagraph();
supporterDrawables.AddRange(textFlow.AddText("Consider becoming an ", format));
supporterDrawables.AddRange(textFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", creationParameters: format));
supporterDrawables.AddRange(textFlow.AddText(" to help support the game", format));
supporterDrawables.Add(heart = textFlow.AddIcon(FontAwesome.fa_heart, t =>
{
t.Padding = new MarginPadding { Left = 5 };
t.TextSize = 12;
t.Colour = colours.Pink;
t.Origin = Anchor.Centre;
}).First());
iconColour = colours.Yellow; iconColour = colours.Yellow;
} }
@ -90,8 +120,15 @@ namespace osu.Game.Screens.Menu
{ {
base.OnEntering(last); base.OnEntering(last);
icon.Delay(1500).FadeColour(iconColour, 200, Easing.OutQuint); icon.Delay(1000).FadeColour(iconColour, 200, Easing.OutQuint);
icon.Delay(1500).MoveToY(icon_y * 1.1f, 100, Easing.OutCirc).Then().MoveToY(icon_y, 100, Easing.InCirc); icon.Delay(1000)
.MoveToY(icon_y * 1.1f, 160, Easing.OutCirc)
.RotateTo(-10, 160, Easing.OutCirc)
.Then()
.MoveToY(icon_y, 160, Easing.InCirc)
.RotateTo(0, 160, Easing.InCirc);
supporterDrawables.ForEach(d => d.FadeOut().Delay(2000).FadeIn(500));
Content Content
.FadeInFromZero(500) .FadeInFromZero(500)
@ -99,6 +136,8 @@ namespace osu.Game.Screens.Menu
.FadeOut(250) .FadeOut(250)
.ScaleTo(0.9f, 250, Easing.InQuint) .ScaleTo(0.9f, 250, Easing.InQuint)
.Finally(d => Push(intro)); .Finally(d => Push(intro));
heart.FlashColour(Color4.White, 750, Easing.OutQuint).Loop();
} }
} }
} }

View File

@ -3,7 +3,6 @@
using osuTK; using osuTK;
using osuTK.Graphics; using osuTK.Graphics;
using osuTK.Graphics.ES30;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Batches; using osu.Framework.Graphics.Batches;
@ -150,7 +149,7 @@ namespace osu.Game.Screens.Menu
private class VisualiserSharedData private class VisualiserSharedData
{ {
public readonly LinearBatch<TexturedVertex2D> VertexBatch = new LinearBatch<TexturedVertex2D>(100 * 4, 10, PrimitiveType.Quads); public readonly QuadBatch<TexturedVertex2D> VertexBatch = new QuadBatch<TexturedVertex2D>(100, 10);
} }
private class VisualisationDrawNode : DrawNode private class VisualisationDrawNode : DrawNode

View File

@ -93,10 +93,14 @@ namespace osu.Game.Screens.Multi.Lounge.Components
Host.BindValueChanged(v => Host.BindValueChanged(v =>
{ {
hostText.Clear(); hostText.Clear();
hostText.AddText("hosted by "); flagContainer.Clear();
hostText.AddLink(v.Username, null, LinkAction.OpenUserProfile, v.Id.ToString(), "Open profile", s => s.Font = "Exo2.0-BoldItalic");
flagContainer.Child = new DrawableFlag(v.Country) { RelativeSizeAxes = Axes.Both }; if (v != null)
{
hostText.AddText("hosted by ");
hostText.AddLink(v.Username, null, LinkAction.OpenUserProfile, v.Id.ToString(), "Open profile", s => s.Font = "Exo2.0-BoldItalic");
flagContainer.Child = new DrawableFlag(v.Country) { RelativeSizeAxes = Axes.Both };
}
}); });
ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}"); ParticipantCount.BindValueChanged(v => summary.Text = $"{v:#,0}{" participant".Pluralize(v == 1)}");

View File

@ -1,7 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Linq; using System;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
@ -13,8 +13,9 @@ using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests;
using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer;
using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Components;
using osu.Game.Users; using osu.Game.Users;
@ -37,11 +38,10 @@ namespace osu.Game.Screens.Multi.Lounge.Components
private Box statusStrip; private Box statusStrip;
private UpdateableBeatmapBackgroundSprite background; private UpdateableBeatmapBackgroundSprite background;
private ParticipantCountDisplay participantCount; private ParticipantCountDisplay participantCount;
private FillFlowContainer topFlow, participantsFlow;
private OsuSpriteText name, status; private OsuSpriteText name, status;
private BeatmapTypeInfo beatmapTypeInfo; private BeatmapTypeInfo beatmapTypeInfo;
private ScrollContainer participantsScroll;
private ParticipantInfo participantInfo; private ParticipantInfo participantInfo;
private MatchParticipants participants;
[Resolved] [Resolved]
private BeatmapManager beatmaps { get; set; } private BeatmapManager beatmaps { get; set; }
@ -58,141 +58,141 @@ namespace osu.Game.Screens.Multi.Lounge.Components
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"343138"), Colour = OsuColour.FromHex(@"343138"),
}, },
topFlow = new FillFlowContainer new GridContainer
{ {
RelativeSizeAxes = Axes.X, RelativeSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Y, RowDimensions = new[]
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
new Container new Dimension(GridSizeMode.AutoSize),
new Dimension(GridSizeMode.Distributed),
},
Content = new[]
{
new Drawable[]
{ {
RelativeSizeAxes = Axes.X, new FillFlowContainer
Height = 200,
Masking = true,
Children = new Drawable[]
{ {
background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both }, RelativeSizeAxes = Axes.X,
new Box AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, new Container
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4.Black.Opacity(0)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Children = new Drawable[]
{ {
participantCount = new ParticipantCountDisplay RelativeSizeAxes = Axes.X,
Height = 200,
Masking = true,
Children = new Drawable[]
{ {
Anchor = Anchor.TopRight, background = new UpdateableBeatmapBackgroundSprite { RelativeSizeAxes = Axes.Both },
Origin = Anchor.TopRight, new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.5f), Color4.Black.Opacity(0)),
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(20),
Children = new Drawable[]
{
participantCount = new ParticipantCountDisplay
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
},
name = new OsuSpriteText
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
TextSize = 30,
},
},
},
}, },
name = new OsuSpriteText },
statusStrip = new Box
{
RelativeSizeAxes = Axes.X,
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{ {
Anchor = Anchor.BottomLeft, new Box
Origin = Anchor.BottomLeft, {
TextSize = 30, RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"28242d"),
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = transition_duration,
Padding = contentPadding,
Spacing = new Vector2(0f, 5f),
Children = new Drawable[]
{
status = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
},
beatmapTypeInfo = new BeatmapTypeInfo(),
},
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = contentPadding,
Children = new Drawable[]
{
participantInfo = new ParticipantInfo(),
}, },
}, },
}, },
}, },
}, },
statusStrip = new Box new Drawable[]
{ {
RelativeSizeAxes = Axes.X, participants = new MatchParticipants
Height = 5,
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{ {
new Box RelativeSizeAxes = Axes.Both,
{ }
RelativeSizeAxes = Axes.Both, }
Colour = OsuColour.FromHex(@"28242d"), }
}, }
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
LayoutDuration = transition_duration,
Padding = contentPadding,
Spacing = new Vector2(0f, 5f),
Children = new Drawable[]
{
status = new OsuSpriteText
{
TextSize = 14,
Font = @"Exo2.0-Bold",
},
beatmapTypeInfo = new BeatmapTypeInfo(),
},
},
},
},
new Container
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Padding = contentPadding,
Children = new Drawable[]
{
participantInfo = new ParticipantInfo(),
},
},
},
},
participantsScroll = new OsuScrollContainer
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Padding = new MarginPadding { Top = contentPadding.Top, Left = 38, Right = 37 },
Children = new[]
{
participantsFlow = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
LayoutDuration = transition_duration,
Spacing = new Vector2(5f),
},
},
},
}; };
participantInfo.Host.BindTo(bindings.Host); participantInfo.Host.BindTo(bindings.Host);
participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount); participantInfo.ParticipantCount.BindTo(bindings.ParticipantCount);
participantInfo.Participants.BindTo(bindings.Participants); participantInfo.Participants.BindTo(bindings.Participants);
participantCount.Participants.BindTo(bindings.Participants); participantCount.Participants.BindTo(bindings.Participants);
participantCount.ParticipantCount.BindTo(bindings.ParticipantCount); participantCount.ParticipantCount.BindTo(bindings.ParticipantCount);
participantCount.MaxParticipants.BindTo(bindings.MaxParticipants); participantCount.MaxParticipants.BindTo(bindings.MaxParticipants);
beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap); beatmapTypeInfo.Beatmap.BindTo(bindings.CurrentBeatmap);
beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset); beatmapTypeInfo.Ruleset.BindTo(bindings.CurrentRuleset);
beatmapTypeInfo.Type.BindTo(bindings.Type); beatmapTypeInfo.Type.BindTo(bindings.Type);
background.Beatmap.BindTo(bindings.CurrentBeatmap); background.Beatmap.BindTo(bindings.CurrentBeatmap);
bindings.Status.BindValueChanged(displayStatus); bindings.Status.BindValueChanged(displayStatus);
bindings.Participants.BindValueChanged(p => participantsFlow.ChildrenEnumerable = p.Select(u => new UserTile(u)));
bindings.Name.BindValueChanged(n => name.Text = n); bindings.Name.BindValueChanged(n => name.Text = n);
Room.BindValueChanged(updateRoom, true); Room.BindValueChanged(updateRoom, true);
} }
private void updateRoom(Room room) private void updateRoom(Room room)
{ {
bindings.Room = room; bindings.Room = room;
participants.Room = room;
if (room != null) if (room != null)
{ {
participantsFlow.FadeIn(transition_duration);
participantCount.FadeIn(transition_duration); participantCount.FadeIn(transition_duration);
beatmapTypeInfo.FadeIn(transition_duration); beatmapTypeInfo.FadeIn(transition_duration);
name.FadeIn(transition_duration); name.FadeIn(transition_duration);
@ -200,7 +200,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
} }
else else
{ {
participantsFlow.FadeOut(transition_duration);
participantCount.FadeOut(transition_duration); participantCount.FadeOut(transition_duration);
beatmapTypeInfo.FadeOut(transition_duration); beatmapTypeInfo.FadeOut(transition_duration);
name.FadeOut(transition_duration); name.FadeOut(transition_duration);
@ -210,13 +209,6 @@ namespace osu.Game.Screens.Multi.Lounge.Components
} }
} }
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
participantsScroll.Height = DrawHeight - topFlow.DrawHeight;
}
private void displayStatus(RoomStatus s) private void displayStatus(RoomStatus s)
{ {
status.Text = s.Message; status.Text = s.Message;
@ -226,39 +218,119 @@ namespace osu.Game.Screens.Multi.Lounge.Components
status.FadeColour(c, transition_duration); status.FadeColour(c, transition_duration);
} }
private class UserTile : Container, IHasTooltip
{
private readonly User user;
public string TooltipText => user.Username;
public UserTile(User user)
{
this.user = user;
Size = new Vector2(70f);
CornerRadius = 5f;
Masking = true;
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"27252d"),
},
new UpdateableAvatar
{
RelativeSizeAxes = Axes.Both,
User = user,
},
};
}
}
private class RoomStatusNoneSelected : RoomStatus private class RoomStatusNoneSelected : RoomStatus
{ {
public override string Message => @"No Room Selected"; public override string Message => @"No Room Selected";
public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8; public override Color4 GetAppropriateColour(OsuColour colours) => colours.Gray8;
} }
private class MatchParticipants : CompositeDrawable
{
private Room room;
private readonly FillFlowContainer fill;
public Room Room
{
get { return room; }
set
{
if (room == value)
return;
room = value;
updateParticipants();
}
}
public MatchParticipants()
{
Padding = new MarginPadding { Horizontal = 10 };
InternalChild = new ScrollContainer
{
RelativeSizeAxes = Axes.Both,
Child = fill = new FillFlowContainer
{
Spacing = new Vector2(10),
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Full,
}
};
}
[Resolved]
private APIAccess api { get; set; }
private GetRoomScoresRequest request;
private void updateParticipants()
{
var roomId = room?.RoomID.Value ?? 0;
request?.Cancel();
// nice little progressive fade
int time = 500;
foreach (var c in fill.Children)
{
c.Delay(500 - time).FadeOut(time, Easing.Out);
time = Math.Max(20, time - 20);
c.Expire();
}
if (roomId == 0) return;
request = new GetRoomScoresRequest(roomId);
request.Success += scores =>
{
if (roomId != room.RoomID.Value)
return;
fill.Clear();
foreach (var s in scores)
fill.Add(new UserTile(s.User));
fill.FadeInFromZero(1000, Easing.OutQuint);
};
api.Queue(request);
}
protected override void Dispose(bool isDisposing)
{
request?.Cancel();
base.Dispose(isDisposing);
}
private class UserTile : CompositeDrawable, IHasTooltip
{
private readonly User user;
public string TooltipText => user.Username;
public UserTile(User user)
{
this.user = user;
Size = new Vector2(70f);
CornerRadius = 5f;
Masking = true;
InternalChildren = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.FromHex(@"27252d"),
},
new UpdateableAvatar
{
RelativeSizeAxes = Axes.Both,
User = user,
},
};
}
}
}
} }
} }

View File

@ -16,17 +16,18 @@ namespace osu.Game.Screens.Multi.Match.Components
{ {
public Action<IEnumerable<APIRoomScoreInfo>> ScoresLoaded; public Action<IEnumerable<APIRoomScoreInfo>> ScoresLoaded;
private readonly Room room; public Room Room
public MatchLeaderboard(Room room)
{ {
this.room = room; get => bindings.Room;
set => bindings.Room = value;
} }
private readonly RoomBindings bindings = new RoomBindings();
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
private void load() private void load()
{ {
room.RoomID.BindValueChanged(id => bindings.RoomID.BindValueChanged(id =>
{ {
if (id == null) if (id == null)
return; return;
@ -38,10 +39,10 @@ namespace osu.Game.Screens.Multi.Match.Components
protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback) protected override APIRequest FetchScores(Action<IEnumerable<APIRoomScoreInfo>> scoresCallback)
{ {
if (room.RoomID == null) if (bindings.RoomID.Value == null)
return null; return null;
var req = new GetRoomScoresRequest(room.RoomID.Value ?? 0); var req = new GetRoomScoresRequest(bindings.RoomID.Value ?? 0);
req.Success += r => req.Success += r =>
{ {

View File

@ -51,6 +51,8 @@ namespace osu.Game.Screens.Multi.Match
MatchChatDisplay chat; MatchChatDisplay chat;
Components.Header header; Components.Header header;
Info info;
GridContainer bottomRow;
MatchSettingsOverlay settings; MatchSettingsOverlay settings;
Children = new Drawable[] Children = new Drawable[]
@ -61,20 +63,21 @@ namespace osu.Game.Screens.Multi.Match
Content = new[] Content = new[]
{ {
new Drawable[] { header = new Components.Header(room) { Depth = -1 } }, new Drawable[] { header = new Components.Header(room) { Depth = -1 } },
new Drawable[] { new Info(room) { OnStart = onStart } }, new Drawable[] { info = new Info(room) { OnStart = onStart } },
new Drawable[] new Drawable[]
{ {
new GridContainer bottomRow = new GridContainer
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
Content = new[] Content = new[]
{ {
new Drawable[] new Drawable[]
{ {
leaderboard = new MatchLeaderboard(room) leaderboard = new MatchLeaderboard
{ {
Padding = new MarginPadding(10), Padding = new MarginPadding(10),
RelativeSizeAxes = Axes.Both RelativeSizeAxes = Axes.Both,
Room = room
}, },
new Container new Container
{ {
@ -108,10 +111,19 @@ namespace osu.Game.Screens.Multi.Match
header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect { Selected = addPlaylistItem }); header.OnRequestSelectBeatmap = () => Push(new MatchSongSelect { Selected = addPlaylistItem });
header.Tabs.Current.ValueChanged += t => header.Tabs.Current.ValueChanged += t =>
{ {
const float fade_duration = 500;
if (t is SettingsMatchPage) if (t is SettingsMatchPage)
{
settings.Show(); settings.Show();
info.FadeOut(fade_duration, Easing.OutQuint);
bottomRow.FadeOut(fade_duration, Easing.OutQuint);
}
else else
{
settings.Hide(); settings.Hide();
info.FadeIn(fade_duration, Easing.OutQuint);
bottomRow.FadeIn(fade_duration, Easing.OutQuint);
}
}; };
chat.Exit += Exit; chat.Exit += Exit;

View File

@ -103,8 +103,8 @@ namespace osu.Game.Screens.Multi.Ranking.Pages
public class ResultsMatchLeaderboard : MatchLeaderboard public class ResultsMatchLeaderboard : MatchLeaderboard
{ {
public ResultsMatchLeaderboard(Room room) public ResultsMatchLeaderboard(Room room)
: base(room)
{ {
Room = room;
} }
protected override bool FadeTop => true; protected override bool FadeTop => true;

View File

@ -39,6 +39,7 @@ namespace osu.Game.Screens.Multi
if (room != null) if (room != null)
{ {
RoomID.UnbindFrom(room.RoomID);
Name.UnbindFrom(room.Name); Name.UnbindFrom(room.Name);
Host.UnbindFrom(room.Host); Host.UnbindFrom(room.Host);
Status.UnbindFrom(room.Status); Status.UnbindFrom(room.Status);
@ -52,22 +53,20 @@ namespace osu.Game.Screens.Multi
Duration.UnbindFrom(room.Duration); Duration.UnbindFrom(room.Duration);
} }
room = value; room = value ?? new Room();
if (room != null) RoomID.BindTo(room.RoomID);
{ Name.BindTo(room.Name);
Name.BindTo(room.Name); Host.BindTo(room.Host);
Host.BindTo(room.Host); Status.BindTo(room.Status);
Status.BindTo(room.Status); Type.BindTo(room.Type);
Type.BindTo(room.Type); Playlist.BindTo(room.Playlist);
Playlist.BindTo(room.Playlist); Participants.BindTo(room.Participants);
Participants.BindTo(room.Participants); ParticipantCount.BindTo(room.ParticipantCount);
ParticipantCount.BindTo(room.ParticipantCount); MaxParticipants.BindTo(room.MaxParticipants);
MaxParticipants.BindTo(room.MaxParticipants); EndDate.BindTo(room.EndDate);
EndDate.BindTo(room.EndDate); Availability.BindTo(room.Availability);
Availability.BindTo(room.Availability); Duration.BindTo(room.Duration);
Duration.BindTo(room.Duration);
}
} }
} }
@ -82,6 +81,7 @@ namespace osu.Game.Screens.Multi
currentRuleset.Value = playlistItem?.Ruleset; currentRuleset.Value = playlistItem?.Ruleset;
} }
public readonly Bindable<int?> RoomID = new Bindable<int?>();
public readonly Bindable<string> Name = new Bindable<string>(); public readonly Bindable<string> Name = new Bindable<string>();
public readonly Bindable<User> Host = new Bindable<User>(); public readonly Bindable<User> Host = new Bindable<User>();
public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>(); public readonly Bindable<RoomStatus> Status = new Bindable<RoomStatus>();

View File

@ -50,7 +50,7 @@ namespace osu.Game.Screens.Play.HUD
protected override void PopOut() => this.FadeOut(fade_duration); protected override void PopOut() => this.FadeOut(fade_duration);
//We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible //We want to handle keyboard inputs all the time in order to trigger ToggleVisibility() when not visible
public override bool HandleNonPositionalInput => true; public override bool PropagateNonPositionalInputSubTree => true;
protected override bool OnKeyDown(KeyDownEvent e) protected override bool OnKeyDown(KeyDownEvent e)
{ {

View File

@ -24,8 +24,6 @@ namespace osu.Game.Screens.Play
{ {
private const int duration = 100; private const int duration = 100;
private readonly Container content;
public readonly KeyCounterCollection KeyCounter; public readonly KeyCounterCollection KeyCounter;
public readonly RollingCounter<int> ComboCounter; public readonly RollingCounter<int> ComboCounter;
public readonly ScoreCounter ScoreCounter; public readonly ScoreCounter ScoreCounter;
@ -37,6 +35,7 @@ namespace osu.Game.Screens.Play
public readonly PlayerSettingsOverlay PlayerSettingsOverlay; public readonly PlayerSettingsOverlay PlayerSettingsOverlay;
private Bindable<bool> showHud; private Bindable<bool> showHud;
private readonly Container visibilityContainer;
private readonly BindableBool replayLoaded = new BindableBool(); private readonly BindableBool replayLoaded = new BindableBool();
private static bool hasShownNotificationOnce; private static bool hasShownNotificationOnce;
@ -45,34 +44,48 @@ namespace osu.Game.Screens.Play
{ {
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.Both;
Add(content = new Container Children = new Drawable[]
{ {
RelativeSizeAxes = Axes.Both, visibilityContainer = new Container
AlwaysPresent = true, // The hud may be hidden but certain elements may need to still be updated
Children = new Drawable[]
{ {
ComboCounter = CreateComboCounter(), RelativeSizeAxes = Axes.Both,
ScoreCounter = CreateScoreCounter(), Children = new Drawable[]
AccuracyCounter = CreateAccuracyCounter(),
HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(),
ModDisplay = CreateModsContainer(),
PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
new FillFlowContainer
{ {
Anchor = Anchor.BottomRight, new Container
Origin = Anchor.BottomRight,
Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{ {
KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock), Anchor = Anchor.TopCentre,
HoldToQuit = CreateHoldForMenuButton(), Origin = Anchor.TopCentre,
} Y = 30,
AutoSizeAxes = Axes.Both,
AutoSizeDuration = 200,
AutoSizeEasing = Easing.Out,
Children = new Drawable[]
{
AccuracyCounter = CreateAccuracyCounter(),
ScoreCounter = CreateScoreCounter(),
ComboCounter = CreateComboCounter(),
},
},
HealthDisplay = CreateHealthDisplay(),
Progress = CreateProgress(),
ModDisplay = CreateModsContainer(),
}
},
PlayerSettingsOverlay = CreatePlayerSettingsOverlay(),
new FillFlowContainer
{
Anchor = Anchor.BottomRight,
Origin = Anchor.BottomRight,
Position = -new Vector2(5, TwoLayerButton.SIZE_RETRACTED.Y),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
KeyCounter = CreateKeyCounter(adjustableClock as IFrameBasedClock),
HoldToQuit = CreateHoldForMenuButton(),
} }
} }
}); };
BindProcessor(scoreProcessor); BindProcessor(scoreProcessor);
BindRulesetContainer(rulesetContainer); BindRulesetContainer(rulesetContainer);
@ -91,7 +104,7 @@ namespace osu.Game.Screens.Play
private void load(OsuConfigManager config, NotificationOverlay notificationOverlay) private void load(OsuConfigManager config, NotificationOverlay notificationOverlay)
{ {
showHud = config.GetBindable<bool>(OsuSetting.ShowInterface); showHud = config.GetBindable<bool>(OsuSetting.ShowInterface);
showHud.ValueChanged += hudVisibility => content.FadeTo(hudVisibility ? 1 : 0, duration); showHud.ValueChanged += hudVisibility => visibilityContainer.FadeTo(hudVisibility ? 1 : 0, duration);
showHud.TriggerChange(); showHud.TriggerChange();
if (!showHud && !hasShownNotificationOnce) if (!showHud && !hasShownNotificationOnce)
@ -159,20 +172,27 @@ namespace osu.Game.Screens.Play
protected virtual RollingCounter<double> CreateAccuracyCounter() => new PercentageCounter protected virtual RollingCounter<double> CreateAccuracyCounter() => new PercentageCounter
{ {
Anchor = Anchor.TopCentre,
Origin = Anchor.TopRight,
Position = new Vector2(0, 35),
TextSize = 20, TextSize = 20,
Margin = new MarginPadding { Right = 140 }, BypassAutoSizeAxes = Axes.X,
Anchor = Anchor.TopLeft,
Origin = Anchor.TopRight,
Margin = new MarginPadding { Top = 5, Right = 20 },
};
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
{
TextSize = 40,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
}; };
protected virtual RollingCounter<int> CreateComboCounter() => new SimpleComboCounter protected virtual RollingCounter<int> CreateComboCounter() => new SimpleComboCounter
{ {
Anchor = Anchor.TopCentre,
Origin = Anchor.TopLeft,
Position = new Vector2(0, 35),
Margin = new MarginPadding { Left = 140 },
TextSize = 20, TextSize = 20,
BypassAutoSizeAxes = Axes.X,
Anchor = Anchor.TopRight,
Origin = Anchor.TopLeft,
Margin = new MarginPadding { Top = 5, Left = 20 },
}; };
protected virtual HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay protected virtual HealthDisplay CreateHealthDisplay() => new StandardHealthDisplay
@ -191,14 +211,6 @@ namespace osu.Game.Screens.Play
AudioClock = offsetClock AudioClock = offsetClock
}; };
protected virtual ScoreCounter CreateScoreCounter() => new ScoreCounter(6)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
TextSize = 40,
Position = new Vector2(0, 30),
};
protected virtual SongProgress CreateProgress() => new SongProgress protected virtual SongProgress CreateProgress() => new SongProgress
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,

View File

@ -32,6 +32,7 @@ using osu.Game.Scoring;
using osu.Game.Screens.Ranking; using osu.Game.Screens.Ranking;
using osu.Game.Skinning; using osu.Game.Skinning;
using osu.Game.Storyboards.Drawables; using osu.Game.Storyboards.Drawables;
using osuTK.Graphics;
namespace osu.Game.Screens.Play namespace osu.Game.Screens.Play
{ {
@ -82,7 +83,7 @@ namespace osu.Game.Screens.Play
protected ScoreProcessor ScoreProcessor; protected ScoreProcessor ScoreProcessor;
protected RulesetContainer RulesetContainer; protected RulesetContainer RulesetContainer;
private HUDOverlay hudOverlay; protected HUDOverlay HUDOverlay;
private FailOverlay failOverlay; private FailOverlay failOverlay;
private DrawableStoryboard storyboard; private DrawableStoryboard storyboard;
@ -199,7 +200,7 @@ namespace osu.Game.Screens.Play
{ {
Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(), Child = RulesetContainer.Cursor?.CreateProxy() ?? new Container(),
}, },
hudOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock) HUDOverlay = new HUDOverlay(ScoreProcessor, RulesetContainer, working, offsetClock, adjustableClock)
{ {
Clock = Clock, // hud overlay doesn't want to use the audio clock directly Clock = Clock, // hud overlay doesn't want to use the audio clock directly
ProcessCustomClock = false, ProcessCustomClock = false,
@ -232,8 +233,8 @@ namespace osu.Game.Screens.Play
} }
}; };
hudOverlay.HoldToQuit.Action = performUserRequestedExit; HUDOverlay.HoldToQuit.Action = performUserRequestedExit;
hudOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded); HUDOverlay.KeyCounter.Visible.BindTo(RulesetContainer.HasReplayLoaded);
RulesetContainer.IsPaused.BindTo(pauseContainer.IsPaused); RulesetContainer.IsPaused.BindTo(pauseContainer.IsPaused);
@ -401,6 +402,7 @@ namespace osu.Game.Screens.Play
{ {
float fadeOutDuration = instant ? 0 : 250; float fadeOutDuration = instant ? 0 : 250;
Content.FadeOut(fadeOutDuration); Content.FadeOut(fadeOutDuration);
Background?.FadeColour(Color4.White, fadeOutDuration, Easing.OutQuint);
} }
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !pauseContainer.IsPaused; protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !pauseContainer.IsPaused;
@ -438,7 +440,7 @@ namespace osu.Game.Screens.Play
.FadeTo(storyboardVisible && BackgroundOpacity > 0 ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint); .FadeTo(storyboardVisible && BackgroundOpacity > 0 ? 1 : 0, BACKGROUND_FADE_DURATION, Easing.OutQuint);
if (storyboardVisible && beatmap.Storyboard.ReplacesBackground) if (storyboardVisible && beatmap.Storyboard.ReplacesBackground)
Background?.FadeTo(0, BACKGROUND_FADE_DURATION, Easing.OutQuint); Background?.FadeColour(Color4.Black, BACKGROUND_FADE_DURATION, Easing.OutQuint);
} }
protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score); protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score);

View File

@ -164,7 +164,7 @@ namespace osu.Game.Screens.Play
protected override void InitializeBackgroundElements() protected override void InitializeBackgroundElements()
{ {
Background?.FadeTo(1, BACKGROUND_FADE_DURATION, Easing.OutQuint); Background?.FadeColour(Color4.White, BACKGROUND_FADE_DURATION, Easing.OutQuint);
Background?.BlurTo(background_blur, BACKGROUND_FADE_DURATION, Easing.OutQuint); Background?.BlurTo(background_blur, BACKGROUND_FADE_DURATION, Easing.OutQuint);
} }

View File

@ -38,7 +38,7 @@ namespace osu.Game.Screens.Play.PlayerSettings
}, },
showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" }, showStoryboardToggle = new PlayerCheckbox { LabelText = "Storyboards" },
beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" }, beatmapSkinsToggle = new PlayerCheckbox { LabelText = "Beatmap skins" },
beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hit sounds" } beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = "Beatmap hitsounds" }
}; };
} }

View File

@ -6,6 +6,7 @@ using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Screens; using osu.Framework.Screens;
using osu.Game.Configuration; using osu.Game.Configuration;
using osu.Game.Graphics;
using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Backgrounds;
using osuTK; using osuTK;
@ -60,14 +61,14 @@ namespace osu.Game.Screens.Play
protected virtual void InitializeBackgroundElements() => UpdateBackgroundElements(); protected virtual void InitializeBackgroundElements() => UpdateBackgroundElements();
/// <summary> /// <summary>
/// Called wen background elements require updates, usually due to a user changing a setting. /// Called when background elements require updates, usually due to a user changing a setting.
/// </summary> /// </summary>
/// <param name="userChange"></param> /// <param name="userChange"></param>
protected virtual void UpdateBackgroundElements() protected virtual void UpdateBackgroundElements()
{ {
if (!IsCurrentScreen) return; if (!IsCurrentScreen) return;
Background?.FadeTo(BackgroundOpacity, BACKGROUND_FADE_DURATION, Easing.OutQuint); Background?.FadeColour(OsuColour.Gray(BackgroundOpacity), BACKGROUND_FADE_DURATION, Easing.OutQuint);
Background?.BlurTo(new Vector2((float)BlurLevel.Value * 25), BACKGROUND_FADE_DURATION, Easing.OutQuint); Background?.BlurTo(new Vector2((float)BlurLevel.Value * 25), BACKGROUND_FADE_DURATION, Easing.OutQuint);
} }
} }

View File

@ -46,10 +46,10 @@ namespace osu.Game.Screens.Play
State = Visibility.Visible; State = Visibility.Visible;
RelativePositionAxes = Axes.Both; RelativePositionAxes = Axes.Both;
RelativeSizeAxes = Axes.Both; RelativeSizeAxes = Axes.X;
Position = new Vector2(0.5f, 0.7f); Position = new Vector2(0.5f, 0.7f);
Size = new Vector2(1, 0.14f); Size = new Vector2(1, 100);
Origin = Anchor.Centre; Origin = Anchor.Centre;
} }

View File

@ -101,7 +101,6 @@ namespace osu.Game.Screens.Select
private readonly Container<DrawableCarouselItem> scrollableContent; private readonly Container<DrawableCarouselItem> scrollableContent;
public Bindable<bool> RightClickScrollingEnabled = new Bindable<bool>(); public Bindable<bool> RightClickScrollingEnabled = new Bindable<bool>();
public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>(); public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>();

View File

@ -297,14 +297,14 @@ namespace osu.Game.Screens.Select
/// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param> /// <param name="performStartAction">Whether to trigger <see cref="OnStart"/>.</param>
public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true) public void FinaliseSelection(BeatmapInfo beatmap = null, bool performStartAction = true)
{ {
// avoid attempting to continue before a selection has been obtained.
// this could happen via a user interaction while the carousel is still in a loading state.
if (Carousel.SelectedBeatmap == null) return;
// if we have a pending filter operation, we want to run it now. // if we have a pending filter operation, we want to run it now.
// it could change selection (ie. if the ruleset has been changed). // it could change selection (ie. if the ruleset has been changed).
Carousel.FlushPendingFilterOperations(); Carousel.FlushPendingFilterOperations();
// avoid attempting to continue before a selection has been obtained.
// this could happen via a user interaction while the carousel is still in a loading state.
if (Carousel.SelectedBeatmap == null) return;
if (beatmap != null) if (beatmap != null)
Carousel.SelectBeatmap(beatmap); Carousel.SelectBeatmap(beatmap);

View File

@ -21,8 +21,6 @@ namespace osu.Game.Skinning
SampleChannel GetSample(string sampleName); SampleChannel GetSample(string sampleName);
TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class; TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration;
TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct;
} }
} }

View File

@ -101,9 +101,10 @@ namespace osu.Game.Skinning
bool hasExtension = filename.Contains('.'); bool hasExtension = filename.Contains('.');
string lastPiece = filename.Split('/').Last(); string lastPiece = filename.Split('/').Last();
var legacyName = filename.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece;
var file = source.Files.Find(f => var file = source.Files.Find(f =>
string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), lastPiece, StringComparison.InvariantCultureIgnoreCase)); string.Equals(hasExtension ? f.Filename : Path.ChangeExtension(f.Filename, null), legacyName, StringComparison.InvariantCultureIgnoreCase));
return file?.FileInfo.StoragePath; return file?.FileInfo.StoragePath;
} }

View File

@ -30,6 +30,9 @@ namespace osu.Game.Skinning
case @"Author": case @"Author":
skin.SkinInfo.Creator = pair.Value; skin.SkinInfo.Creator = pair.Value;
break; break;
case @"CursorExpand":
skin.CursorExpand = pair.Value != "0";
break;
} }
break; break;

View File

@ -43,22 +43,14 @@ namespace osu.Game.Skinning
return fallbackSource?.GetSample(sampleName); return fallbackSource?.GetSample(sampleName);
} }
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
{ {
TValue? val = null; TValue val;
if ((source as Skin)?.Configuration is TConfiguration conf) if ((source as Skin)?.Configuration is TConfiguration conf)
val = query?.Invoke(conf); if (beatmapSkins && (val = query.Invoke(conf)) != null)
return val;
return val ?? fallbackSource?.GetValue(query); return fallbackSource == null ? default : fallbackSource.GetValue(query);
}
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class
{
TValue val = null;
if ((source as Skin)?.Configuration is TConfiguration conf)
val = query?.Invoke(conf);
return val ?? fallbackSource?.GetValue(query);
} }
private readonly ISkinSource source; private readonly ISkinSource source;

View File

@ -22,11 +22,8 @@ namespace osu.Game.Skinning
public abstract Texture GetTexture(string componentName); public abstract Texture GetTexture(string componentName);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null; => Configuration is TConfiguration conf ? query.Invoke(conf) : default;
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct
=> Configuration is TConfiguration conf ? query?.Invoke(conf) : null;
protected Skin(SkinInfo skin) protected Skin(SkinInfo skin)
{ {

View File

@ -16,5 +16,7 @@ namespace osu.Game.Skinning
public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>(); public Dictionary<string, Color4> CustomColours { get; set; } = new Dictionary<string, Color4>();
public string HitCircleFont { get; set; } = "default"; public string HitCircleFont { get; set; } = "default";
public bool? CursorExpand { get; set; } = true;
} }
} }

View File

@ -116,8 +116,6 @@ namespace osu.Game.Skinning
public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName); public SampleChannel GetSample(string sampleName) => CurrentSkin.Value.GetSample(sampleName);
public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration where TValue : class => CurrentSkin.Value.GetValue(query); public TValue GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue> query) where TConfiguration : SkinConfiguration => CurrentSkin.Value.GetValue(query);
public TValue? GetValue<TConfiguration, TValue>(Func<TConfiguration, TValue?> query) where TConfiguration : SkinConfiguration where TValue : struct => CurrentSkin.Value.GetValue(query);
} }
} }

View File

@ -44,7 +44,7 @@ namespace osu.Game.Tests.Visual
{ {
Player p = null; Player p = null;
AddStep(ruleset.RulesetInfo.Name, () => p = loadPlayerFor(ruleset)); AddStep(ruleset.RulesetInfo.Name, () => p = loadPlayerFor(ruleset));
AddUntilStep(() => ContinueCondition(p)); AddCheckSteps(() => p);
} }
else else
{ {
@ -52,7 +52,7 @@ namespace osu.Game.Tests.Visual
{ {
Player p = null; Player p = null;
AddStep(r.Name, () => p = loadPlayerFor(r)); AddStep(r.Name, () => p = loadPlayerFor(r));
AddUntilStep(() => ContinueCondition(p)); AddCheckSteps(() => p);
AddUntilStep(() => AddUntilStep(() =>
{ {
@ -79,7 +79,10 @@ namespace osu.Game.Tests.Visual
} }
} }
protected virtual bool ContinueCondition(Player player) => player.IsLoaded; protected virtual void AddCheckSteps(Func<Player> player)
{
AddUntilStep(() => player().IsLoaded, "player loaded");
}
protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo); protected virtual IBeatmap CreateBeatmap(Ruleset ruleset) => new TestBeatmap(ruleset.RulesetInfo);

View File

@ -78,6 +78,5 @@ namespace osu.Game.Users
[JsonProperty(@"country")] [JsonProperty(@"country")]
public int? Country; public int? Country;
} }
} }
} }

View File

@ -15,10 +15,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup Label="Package References"> <ItemGroup Label="Package References">
<PackageReference Include="Humanizer" Version="2.5.16" /> <PackageReference Include="Humanizer" Version="2.5.16" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.1.4" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.0" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
<PackageReference Include="ppy.osu.Framework" Version="0.0.7939" /> <PackageReference Include="ppy.osu.Framework" Version="2019.122.0" />
<PackageReference Include="SharpCompress" Version="0.22.0" /> <PackageReference Include="SharpCompress" Version="0.22.0" />
<PackageReference Include="NUnit" Version="3.11.0" /> <PackageReference Include="NUnit" Version="3.11.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" /> <PackageReference Include="SharpRaven" Version="2.4.0" />