1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-04 00:14:12 +08:00

Compare commits

...

209 Commits

154 changed files with 1453 additions and 840 deletions
+56 -11
View File
@@ -11,25 +11,70 @@ We are accepting bug reports (please report with as much detail as possible). Fe
# Requirements
- 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/).
# 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)
- 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.
## Downloading the source code
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
+1 -1
View File
@@ -97,7 +97,7 @@ namespace osu.Desktop
private void fileDrop(object sender, FileDropEventArgs e)
{
var filePaths = new[] { e.FileName };
var filePaths = e.FileNames;
var firstExtension = Path.GetExtension(filePaths.First());
+2 -3
View File
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json;
using osu.Framework;
@@ -77,10 +76,10 @@ namespace osu.Desktop.Updater
switch (RuntimeInfo.OS)
{
case RuntimeInfo.Platform.Windows:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".exe"));
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".exe"));
break;
case RuntimeInfo.Platform.MacOsx:
bestAsset = release.Assets?.FirstOrDefault(f => f.Name.EndsWith(".app.zip"));
bestAsset = release.Assets?.Find(f => f.Name.EndsWith(".app.zip"));
break;
}
+2 -2
View File
@@ -28,8 +28,8 @@
<ItemGroup Label="Package References">
<PackageReference Include="System.IO.Packaging" Version="4.5.0" />
<PackageReference Include="ppy.squirrel.windows" Version="1.9.0.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.1" />
</ItemGroup>
<ItemGroup Label="Resources">
<EmbeddedResource Include="lazer.ico" />
@@ -26,7 +26,6 @@ namespace osu.Game.Rulesets.Catch.Tests
}
};
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 });
@@ -19,7 +19,6 @@ namespace osu.Game.Rulesets.Catch.Tests
{
var beatmap = new Beatmap { BeatmapInfo = { Ruleset = ruleset.RulesetInfo } };
for (int i = 0; i < 512; i++)
if (i % 5 < 3)
beatmap.HitObjects.Add(new Fruit { X = i % 10 < 5 ? 0.02f : 0.98f, StartTime = i * 100, NewCombo = i % 8 == 0 });
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.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" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty
{
public class CatchDifficultyCalculator : DifficultyCalculator
{
/// <summary>
/// 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.
@@ -0,0 +1,23 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
namespace osu.Game.Rulesets.Catch.Objects
{
public class CatchHitWindows : HitWindows
{
public override bool IsHitResultAllowed(HitResult result)
{
switch (result)
{
case HitResult.Perfect:
case HitResult.Miss:
return true;
}
return false;
}
}
}
@@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawable
base.SkinChanged(skin, allowFallback);
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;
@@ -5,6 +5,7 @@ using System;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -41,5 +42,7 @@ namespace osu.Game.Rulesets.Catch.Scoring
Health.Value += Math.Max(result.Judgement.HealthIncreaseFor(result) - hpDrainRate, 0) * harshness;
}
protected override HitWindows CreateHitWindows() => new CatchHitWindows();
}
}
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.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" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -180,7 +180,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
foreach (var obj in newPattern.HitObjects)
yield return obj;
}
}
@@ -53,7 +53,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty
if (!calculateStrainValues(difficultyHitObjects, timeRate))
return new DifficultyAttributes(mods, 0);
double starRating = calculateDifficulty(difficultyHitObjects, timeRate) * star_scaling_factor;
return new ManiaDifficultyAttributes(mods, starRating)
@@ -3,14 +3,14 @@
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
using System.Linq;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Beatmaps;
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 Acronym => "MR";
@@ -18,11 +18,11 @@ namespace osu.Game.Rulesets.Mania.Mods
public override double ScoreMultiplier => 1;
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);
}
}
}
@@ -4,15 +4,15 @@
using System.Linq;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.MathUtils;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
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 Acronym => "RD";
@@ -21,12 +21,12 @@ namespace osu.Game.Rulesets.Mania.Mods
public override string Description => @"Shuffle around the keys!";
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();
rulesetContainer.Objects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
beatmap.HitObjects.OfType<ManiaHitObject>().ForEach(h => h.Column = shuffledColumns[h.Column]);
}
}
}
@@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Mania.Objects
private readonly int beatmapColumnCount;
private readonly double endTime;
private readonly double[] heldUntil;
@@ -5,6 +5,7 @@ using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Mania.Judgements;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
@@ -157,5 +158,7 @@ namespace osu.Game.Rulesets.Mania.Scoring
}
}
}
protected override HitWindows CreateHitWindows() => new ManiaHitWindows();
}
}
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.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" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Graphics;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Objects.Types;
@@ -23,60 +24,70 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
var osuBeatmap = (Beatmap<OsuHitObject>)Beatmap;
// Reset stacking
foreach (var h in osuBeatmap.HitObjects)
h.StackHeight = 0;
if (osuBeatmap.HitObjects.Count > 0)
{
// Reset stacking
foreach (var h in osuBeatmap.HitObjects)
h.StackHeight = 0;
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(osuBeatmap);
else
applyStackingOld(osuBeatmap);
if (Beatmap.BeatmapInfo.BeatmapVersion >= 6)
applyStacking(osuBeatmap, 0, osuBeatmap.HitObjects.Count - 1);
else
applyStackingOld(osuBeatmap);
}
}
private void applyStacking(Beatmap<OsuHitObject> beatmap)
private void applyStacking(Beatmap<OsuHitObject> beatmap, int startIndex, int endIndex)
{
// Extend the end index to include objects they are stacked on
int extendedEndIndex = beatmap.HitObjects.Count - 1;
for (int i = beatmap.HitObjects.Count - 1; i >= 0; i--)
if (startIndex > endIndex) throw new ArgumentOutOfRangeException(nameof(startIndex), $"{nameof(startIndex)} cannot be greater than {nameof(endIndex)}.");
if (startIndex < 0) throw new ArgumentOutOfRangeException(nameof(startIndex), $"{nameof(startIndex)} cannot be less than 0.");
if (endIndex < 0) throw new ArgumentOutOfRangeException(nameof(endIndex), $"{nameof(endIndex)} cannot be less than 0.");
int extendedEndIndex = endIndex;
if (endIndex < beatmap.HitObjects.Count - 1)
{
int stackBaseIndex = i;
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
// Extend the end index to include objects they are stacked on
for (int i = endIndex; i >= startIndex; i--)
{
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
if (stackBaseObject is Spinner) break;
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner)
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance ||
stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
int stackBaseIndex = i;
for (int n = stackBaseIndex + 1; n < beatmap.HitObjects.Count; n++)
{
stackBaseIndex = n;
OsuHitObject stackBaseObject = beatmap.HitObjects[stackBaseIndex];
if (stackBaseObject is Spinner) break;
// HitObjects after the specified update range haven't been reset yet
objectN.StackHeight = 0;
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner)
continue;
double endTime = (stackBaseObject as IHasEndTime)?.EndTime ?? stackBaseObject.StartTime;
double stackThreshold = objectN.TimePreempt * beatmap.BeatmapInfo.StackLeniency;
if (objectN.StartTime - endTime > stackThreshold)
//We are no longer within stacking range of the next object.
break;
if (Vector2Extensions.Distance(stackBaseObject.Position, objectN.Position) < stack_distance
|| stackBaseObject is Slider && Vector2Extensions.Distance(stackBaseObject.EndPosition, objectN.Position) < stack_distance)
{
stackBaseIndex = n;
// HitObjects after the specified update range haven't been reset yet
objectN.StackHeight = 0;
}
}
}
if (stackBaseIndex > extendedEndIndex)
{
extendedEndIndex = stackBaseIndex;
if (extendedEndIndex == beatmap.HitObjects.Count - 1)
break;
if (stackBaseIndex > extendedEndIndex)
{
extendedEndIndex = stackBaseIndex;
if (extendedEndIndex == beatmap.HitObjects.Count - 1)
break;
}
}
}
//Reverse pass for stack calculation.
int extendedStartIndex = 0;
for (int i = extendedEndIndex; i > 0; i--)
int extendedStartIndex = startIndex;
for (int i = extendedEndIndex; i > startIndex; i--)
{
int n = i;
/* We should check every note which has not yet got a stack.
@@ -155,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
/* We have hit the first slider in a possible stack.
* From this point on, we ALWAYS stack positive regardless.
*/
while (--n >= 0)
while (--n >= startIndex)
{
OsuHitObject objectN = beatmap.HitObjects[n];
if (objectN is Spinner) continue;
@@ -57,6 +57,10 @@ namespace osu.Game.Rulesets.Osu.Difficulty
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 speedRating = Math.Sqrt(skills[1].DifficultyValue()) * difficulty_multiplier;
double starRating = aimRating + speedRating + Math.Abs(aimRating - speedRating) / 2;
@@ -105,13 +105,9 @@ namespace osu.Game.Rulesets.Osu.Difficulty
double approachRateFactor = 1.0f;
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)
{
// 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);
}
@@ -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.
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))
{
@@ -152,13 +148,19 @@ namespace osu.Game.Rulesets.Osu.Difficulty
if (beatmapMaxCombo > 0)
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))
speedValue *= 1.18f;
speedValue *= 1.0f + 0.04f * (12.0f - Attributes.ApproachRate);
// 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
speedValue *= 0.98f + Math.Pow(Attributes.OverallDifficulty, 2) / 2500;
speedValue *= 0.96f + Math.Pow(Attributes.OverallDifficulty, 2) / 1600;
return speedValue;
}
@@ -186,7 +188,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty
accuracyValue *= Math.Min(1.15f, Math.Pow(amountHitObjectsWithAccuracy / 1000.0f, 0.3f));
if (mods.Any(m => m is OsuModHidden))
accuracyValue *= 1.02f;
accuracyValue *= 1.08f;
if (mods.Any(m => m is OsuModFlashlight))
accuracyValue *= 1.02f;
@@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
base.SkinChanged(skin, allowFallback);
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);
@@ -101,7 +101,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables
break;
}
float aimRotation = MathHelper.RadiansToDegrees((float)Math.Atan2(aimRotationVector.Y - Position.Y, aimRotationVector.X - Position.X));
while (Math.Abs(aimRotation - Rotation) > 180)
aimRotation += aimRotation < Rotation ? 360 : -360;
@@ -14,6 +14,7 @@ using osu.Game.Configuration;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osuTK.Graphics;
using osu.Game.Skinning;
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)
{
if (userTriggered || Time.Current < slider.EndTime)
+29 -5
View File
@@ -7,6 +7,7 @@ using osu.Game.Rulesets.Objects.Types;
using System.Collections.Generic;
using osu.Game.Rulesets.Objects;
using System.Linq;
using osu.Framework.Caching;
using osu.Framework.Configuration;
using osu.Game.Audio;
using osu.Game.Beatmaps;
@@ -26,8 +27,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public double EndTime => StartTime + this.SpanCount() * Path.Distance / Velocity;
public double Duration => EndTime - StartTime;
private Cached<Vector2> endPositionCache;
public override Vector2 EndPosition => endPositionCache.IsValid ? endPositionCache.Value : endPositionCache.Value = Position + this.CurvePositionAt(1);
public Vector2 StackedPositionAt(double t) => StackedPosition + this.CurvePositionAt(t);
public override Vector2 EndPosition => Position + this.CurvePositionAt(1);
public override int ComboIndex
{
@@ -56,7 +60,11 @@ namespace osu.Game.Rulesets.Osu.Objects
public SliderPath Path
{
get => PathBindable.Value;
set => PathBindable.Value = value;
set
{
PathBindable.Value = value;
endPositionCache.Invalidate();
}
}
public double Distance => Path.Distance;
@@ -73,6 +81,8 @@ namespace osu.Game.Rulesets.Osu.Objects
if (TailCircle != null)
TailCircle.Position = EndPosition;
endPositionCache.Invalidate();
}
}
@@ -92,7 +102,17 @@ namespace osu.Game.Rulesets.Osu.Objects
public List<List<SampleInfo>> NodeSamples { get; set; } = new List<List<SampleInfo>>();
public int RepeatCount { get; set; }
private int repeatCount;
public int RepeatCount
{
get => repeatCount;
set
{
repeatCount = value;
endPositionCache.Invalidate();
}
}
/// <summary>
/// The length of one span of this <see cref="Slider"/>.
@@ -169,7 +189,11 @@ namespace osu.Game.Rulesets.Osu.Objects
private void createTicks()
{
var length = Path.Distance;
// A very lenient maximum length of a slider for ticks to be generated.
// This exists for edge cases such as /b/1573664 where the beatmap has been edited by the user, and should never be reached in normal usage.
const double max_length = 100000;
var length = Math.Min(max_length, Path.Distance);
var tickDistance = MathHelper.Clamp(TickDistance, 0, length);
if (tickDistance == 0) return;
@@ -191,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Objects
var distanceProgress = d / length;
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress;
var firstSample = Samples.FirstOrDefault(s => s.Name == SampleInfo.HIT_NORMAL)
var firstSample = Samples.Find(s => s.Name == SampleInfo.HIT_NORMAL)
?? Samples.FirstOrDefault(); // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
var sampleList = new List<SampleInfo>();
@@ -5,11 +5,11 @@ using System.Collections.Generic;
using osu.Framework.Extensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Judgements;
using osu.Game.Rulesets.Osu.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.Osu.Scoring
{
@@ -22,7 +22,6 @@ namespace osu.Game.Rulesets.Osu.Scoring
private float hpDrainRate;
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
private readonly Dictionary<ComboResult, int> comboResultCounts = new Dictionary<ComboResult, int>();
protected override void ApplyBeatmap(Beatmap<OsuHitObject> beatmap)
@@ -35,21 +34,9 @@ namespace osu.Game.Rulesets.Osu.Scoring
protected override void Reset(bool storeResults)
{
base.Reset(storeResults);
scoreResultCounts.Clear();
comboResultCounts.Clear();
}
public override void PopulateScore(ScoreInfo score)
{
base.PopulateScore(score);
score.Statistics[HitResult.Great] = scoreResultCounts.GetOrDefault(HitResult.Great);
score.Statistics[HitResult.Good] = scoreResultCounts.GetOrDefault(HitResult.Good);
score.Statistics[HitResult.Meh] = scoreResultCounts.GetOrDefault(HitResult.Meh);
score.Statistics[HitResult.Miss] = scoreResultCounts.GetOrDefault(HitResult.Miss);
}
private const double harshness = 0.01;
protected override void ApplyResult(JudgementResult result)
@@ -59,10 +46,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
var osuResult = (OsuJudgementResult)result;
if (result.Type != HitResult.None)
{
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
comboResultCounts[osuResult.ComboType] = comboResultCounts.GetOrDefault(osuResult.ComboType) + 1;
}
switch (result.Type)
{
@@ -89,5 +73,7 @@ namespace osu.Game.Rulesets.Osu.Scoring
}
protected override JudgementResult CreateResult(Judgement judgement) => new OsuJudgementResult(judgement);
protected override HitWindows CreateHitWindows() => new OsuHitWindows();
}
}
@@ -39,10 +39,13 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
private int downCount;
private const float pressed_scale = 1.2f;
private const float released_scale = 1f;
private float targetScale => downCount > 0 ? pressed_scale : released_scale;
private void updateExpandedState()
{
if (downCount > 0)
(ActiveCursor as OsuCursor)?.Expand();
else
(ActiveCursor as OsuCursor)?.Contract();
}
public bool OnPressed(OsuAction action)
{
@@ -51,7 +54,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
downCount++;
ActiveCursor.ScaleTo(released_scale).ScaleTo(targetScale, 100, Easing.OutQuad);
updateExpandedState();
break;
}
@@ -65,7 +68,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
case OsuAction.LeftButton:
case OsuAction.RightButton:
if (--downCount == 0)
ActiveCursor.ScaleTo(targetScale, 200, Easing.OutQuad);
updateExpandedState();
break;
}
@@ -77,92 +80,106 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor
protected override void PopIn()
{
fadeContainer.FadeTo(1, 300, Easing.OutQuint);
ActiveCursor.ScaleTo(targetScale, 400, Easing.OutQuint);
ActiveCursor.ScaleTo(1, 400, Easing.OutQuint);
}
protected override void PopOut()
{
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<bool> autoCursorScale;
private readonly IBindable<WorkingBeatmap> beatmap = new Bindable<WorkingBeatmap>();
private Container expandTarget;
private Drawable scaleTarget;
public OsuCursor()
{
Origin = Anchor.Centre;
Size = new Vector2(42);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindableBeatmap beatmap)
protected override void SkinChanged(ISkinSource skin, bool allowFallback)
{
Child = cursorContainer = new SkinnableDrawable("cursor", _ => new CircularContainer
cursorExpand = skin.GetValue<SkinConfiguration, bool>(s => s.CursorExpand ?? true);
}
[BackgroundDependencyLoader]
private void load(OsuConfigManager config, IBindable<WorkingBeatmap> beatmap)
{
InternalChild = expandTarget = new Container
{
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,
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);
@@ -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);
}
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);
}
}
}
@@ -4,7 +4,7 @@
<PackageReference Include="Appveyor.TestLogger" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.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" />
</ItemGroup>
<PropertyGroup Label="Project">
@@ -135,7 +135,7 @@ namespace osu.Game.Rulesets.Taiko.Objects.Drawables
{
if (userTriggered)
{
var nextTick = ticks.FirstOrDefault(j => !j.IsHit);
var nextTick = ticks.Find(j => !j.IsHit);
nextTick?.TriggerResult(HitResult.Great);
@@ -3,6 +3,7 @@
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.Taiko.Objects;
using osu.Game.Rulesets.UI;
@@ -65,5 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring
Health.Value = 0;
}
protected override HitWindows CreateHitWindows() => new TaikoHitWindows();
}
}
@@ -149,7 +149,6 @@ namespace osu.Game.Tests.Beatmaps.Formats
using (var stream = Resource.OpenResource(filename))
using (var sr = new StreamReader(stream))
{
var legacyDecoded = new LegacyBeatmapDecoder { ApplyOffsets = false }.Decode(sr);
using (var ms = new MemoryStream())
using (var sw = new StreamWriter(ms))
@@ -40,7 +40,6 @@ namespace osu.Game.Tests.Visual
typeof(DrawableCarouselBeatmapSet),
};
private readonly Stack<BeatmapSetInfo> selectedSets = new Stack<BeatmapSetInfo>();
private readonly HashSet<int> eagerSelectedIDs = new HashSet<int>();
@@ -148,7 +147,7 @@ namespace osu.Game.Tests.Visual
private bool selectedBeatmapVisible()
{
var currentlySelected = carousel.Items.FirstOrDefault(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected);
var currentlySelected = carousel.Items.Find(s => s.Item is CarouselBeatmap && s.Item.State == CarouselItemState.Selected);
if (currentlySelected == null)
return true;
return currentlySelected.Item.Visible;
@@ -55,7 +55,6 @@ namespace osu.Game.Tests.Visual
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
scores = new[]
{
new APIScoreInfo
@@ -16,7 +16,6 @@ namespace osu.Game.Tests.Visual
public TestCaseBreadcrumbs()
{
Add(breadcrumbs = new BreadcrumbControl<BreadcrumbTab>
{
Anchor = Anchor.Centre,
+1 -1
View File
@@ -55,7 +55,7 @@ namespace osu.Game.Tests.Visual
linkColour = colours.Blue;
var chatManager = new ChannelManager();
BindableCollection<Channel> availableChannels = (BindableCollection<Channel>)chatManager.AvailableChannels;
BindableList<Channel> availableChannels = (BindableList<Channel>)chatManager.AvailableChannels;
availableChannels.Add(new Channel { Name = "#english"});
availableChannels.Add(new Channel { Name = "#japanese" });
Dependencies.Cache(chatManager);
@@ -85,7 +85,7 @@ namespace osu.Game.Tests.Visual
}
[BackgroundDependencyLoader]
private void load(IAdjustableClock adjustableClock, IBindableBeatmap beatmap)
private void load(IAdjustableClock adjustableClock, IBindable<WorkingBeatmap> beatmap)
{
this.adjustableClock = adjustableClock;
this.beatmap.BindTo(beatmap);
@@ -39,7 +39,6 @@ namespace osu.Game.Tests.Visual
},
};
AddStep("Add random", () =>
{
Key key = (Key)((int)Key.A + RNG.Next(26));
@@ -71,8 +71,10 @@ namespace osu.Game.Tests.Visual
private class TestRoomManager : IRoomManager
{
public readonly BindableCollection<Room> Rooms = new BindableCollection<Room>();
IBindableCollection<Room> IRoomManager.Rooms => Rooms;
public event Action RoomsUpdated;
public readonly BindableList<Room> Rooms = new BindableList<Room>();
IBindableList<Room> IRoomManager.Rooms => Rooms;
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null) => Rooms.Add(room);
@@ -17,12 +17,13 @@ namespace osu.Game.Tests.Visual
{
public TestCaseMatchLeaderboard()
{
Add(new MatchLeaderboard(new Room { RoomID = { Value = 3 } })
Add(new MatchLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
Size = new Vector2(550f, 450f),
Scope = MatchLeaderboardScope.Overall,
Room = new Room { RoomID = { Value = 3 } }
});
}
@@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual
this.room = room;
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap, room) };
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value, room) };
}
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
@@ -136,7 +136,9 @@ namespace osu.Game.Tests.Visual
public Func<Room, bool> CreateRequested;
public IBindableCollection<Room> Rooms { get; } = null;
public event Action RoomsUpdated;
public IBindableList<Room> Rooms { get; } = null;
public void CreateRoom(Room room, Action<Room> onSuccess = null, Action<string> onError = null)
{
@@ -49,7 +49,6 @@ namespace osu.Game.Tests.Visual
manager.UnreadCount.ValueChanged += count => { displayedCount.Text = $"displayed count: {count}"; };
setState(Visibility.Visible);
AddStep(@"simple #1", sendHelloNotification);
AddStep(@"simple #2", sendAmazingNotification);
@@ -75,7 +74,6 @@ namespace osu.Game.Tests.Visual
checkProgressingCount(0);
setState(Visibility.Visible);
//AddStep(@"barrage", () => sendBarrage());
@@ -111,7 +109,7 @@ namespace osu.Game.Tests.Visual
if (progressingNotifications.Count(n => n.State == ProgressNotificationState.Active) < 3)
{
var p = progressingNotifications.FirstOrDefault(n => n.State == ProgressNotificationState.Queued);
var p = progressingNotifications.Find(n => n.State == ProgressNotificationState.Queued);
if (p != null)
p.State = ProgressNotificationState.Active;
}
@@ -57,11 +57,19 @@ namespace osu.Game.Tests.Visual
private class TestSongSelect : PlaySongSelect
{
public Action StartRequested;
public new Bindable<RulesetInfo> Ruleset => base.Ruleset;
public WorkingBeatmap CurrentBeatmap => Beatmap.Value;
public WorkingBeatmap CurrentBeatmapDetailsBeatmap => BeatmapDetails.Beatmap;
public new BeatmapCarousel Carousel => base.Carousel;
protected override bool OnStart()
{
StartRequested?.Invoke();
return base.OnStart();
}
}
private TestSongSelect songSelect;
@@ -182,6 +190,27 @@ namespace osu.Game.Tests.Visual
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 static int importId;
@@ -57,5 +57,4 @@ namespace osu.Game.Tests.Visual
}
}
}
}
+1 -2
View File
@@ -5,7 +5,6 @@ using System.ComponentModel;
using System.Linq;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
@@ -23,7 +22,7 @@ namespace osu.Game.Tests.Visual
// Reset the mods
Beatmap.Value.Mods.Value = Beatmap.Value.Mods.Value.Where(m => !(m is ModAutoplay));
return new ReplayPlayer(new Score { Replay = dummyRulesetContainer.Replay });
return new ReplayPlayer(dummyRulesetContainer.ReplayScore);
}
}
}
@@ -7,7 +7,7 @@ using osu.Game.Screens.Play;
namespace osu.Game.Tests.Visual
{
[TestFixture]
public class TestCaseSkipButton : OsuTestCase
public class TestCaseSkipOverlay : OsuTestCase
{
protected override void LoadComplete()
{
+1 -1
View File
@@ -5,7 +5,7 @@
<PackageReference Include="DeepEqual" Version="2.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.9.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" />
</ItemGroup>
<PropertyGroup Label="Project">
+7
View File
@@ -2,6 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
@@ -9,6 +10,7 @@ using Newtonsoft.Json;
using osu.Game.Database;
using osu.Game.IO.Serialization;
using osu.Game.Rulesets;
using osu.Game.Scoring;
namespace osu.Game.Beatmaps
{
@@ -112,6 +114,11 @@ namespace osu.Game.Beatmaps
[JsonProperty("difficulty_rating")]
public double StarDifficulty { get; set; }
/// <summary>
/// Currently only populated for beatmap deletion. Use <see cref="ScoreManager"/> to query scores.
/// </summary>
public List<ScoreInfo> Scores { get; set; }
public override string ToString() => $"{Metadata} [{Version}]";
public bool Equals(BeatmapInfo other)
+1 -1
View File
@@ -36,7 +36,7 @@ namespace osu.Game.Beatmaps
public string Hash { get; set; }
public string StoryboardFile => Files?.FirstOrDefault(f => f.Filename.EndsWith(".osb"))?.Filename;
public string StoryboardFile => Files?.Find(f => f.Filename.EndsWith(".osb"))?.Filename;
public List<BeatmapSetFileInfo> Files { get; set; }
+2 -1
View File
@@ -64,7 +64,8 @@ namespace osu.Game.Beatmaps
base.AddIncludesForDeletion(query)
.Include(s => s.Beatmaps).ThenInclude(b => b.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.BaseDifficulty)
.Include(s => s.Metadata);
.Include(s => s.Metadata)
.Include(s => s.Beatmaps).ThenInclude(b => b.Scores);
protected override IQueryable<BeatmapSetInfo> AddIncludesForConsumption(IQueryable<BeatmapSetInfo> query) =>
base.AddIncludesForConsumption(query)
+2 -5
View File
@@ -12,9 +12,9 @@ namespace osu.Game.Beatmaps
{
/// <summary>
/// A <see cref="Bindable{WorkingBeatmap}"/> for the <see cref="OsuGame"/> beatmap.
/// This should be used sparingly in-favour of <see cref="IBindableBeatmap"/>.
/// This should be used sparingly in-favour of <see cref="IBindable<WorkingBeatmap>"/>.
/// </summary>
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>, IBindableBeatmap
public abstract class BindableBeatmap : NonNullableBindable<WorkingBeatmap>
{
private AudioManager audioManager;
private WorkingBeatmap lastBeatmap;
@@ -62,9 +62,6 @@ namespace osu.Game.Beatmaps
lastBeatmap = beatmap;
}
[NotNull]
IBindableBeatmap IBindableBeatmap.GetBoundCopy() => GetBoundCopy();
/// <summary>
/// Retrieve a new <see cref="BindableBeatmap"/> instance weakly bound to this <see cref="BindableBeatmap"/>.
/// If you are further binding to events of the retrieved <see cref="BindableBeatmap"/>, ensure a local reference is held.
@@ -23,7 +23,7 @@ namespace osu.Game.Beatmaps.Drawables
}
[BackgroundDependencyLoader]
private void load(TextureStore textures)
private void load(LargeTextureStore textures)
{
string resource = null;
@@ -25,23 +25,26 @@ namespace osu.Game.Beatmaps.Drawables
protected override Drawable CreateDrawable(BeatmapInfo model)
{
Drawable drawable;
return new DelayedLoadUnloadWrapper(() => {
Drawable drawable;
var localBeatmap = beatmaps.GetWorkingBeatmap(model);
var localBeatmap = beatmaps.GetWorkingBeatmap(model);
if (localBeatmap.BeatmapInfo.ID == 0 && model?.BeatmapSet?.OnlineInfo != null)
drawable = new BeatmapSetCover(model.BeatmapSet);
else
drawable = new BeatmapBackgroundSprite(localBeatmap);
if (localBeatmap.BeatmapInfo.ID == 0 && model?.BeatmapSet?.OnlineInfo != null)
drawable = new BeatmapSetCover(model.BeatmapSet);
else
drawable = new BeatmapBackgroundSprite(localBeatmap);
drawable.RelativeSizeAxes = Axes.Both;
drawable.Anchor = Anchor.Centre;
drawable.Origin = Anchor.Centre;
drawable.FillMode = FillMode.Fill;
drawable.RelativeSizeAxes = Axes.Both;
drawable.Anchor = Anchor.Centre;
drawable.Origin = Anchor.Centre;
drawable.FillMode = FillMode.Fill;
drawable.OnLoadComplete = d => d.FadeInFromZero(400);
return drawable;
return drawable;
}, 500, 10000);
}
protected override double FadeDuration => 400;
protected override double FadeDuration => 0;
}
}
-19
View File
@@ -1,19 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Configuration;
namespace osu.Game.Beatmaps
{
/// <summary>
/// Read-only interface for the <see cref="OsuGame"/> beatmap.
/// </summary>
public interface IBindableBeatmap : IBindable<WorkingBeatmap>
{
/// <summary>
/// Retrieve a new <see cref="IBindableBeatmap"/> instance weakly bound to this <see cref="IBindableBeatmap"/>.
/// If you are further binding to events of the retrieved <see cref="IBindableBeatmap"/>, ensure a local reference is held.
/// </summary>
IBindableBeatmap GetBoundCopy();
}
}
+1 -1
View File
@@ -151,7 +151,7 @@ namespace osu.Game.Beatmaps
public bool WaveformLoaded => waveform.IsResultAvailable;
public Waveform Waveform => waveform.Value;
protected virtual Waveform GetWaveform() => new Waveform();
protected virtual Waveform GetWaveform() => new Waveform(null);
private readonly RecyclableLazy<Waveform> waveform;
public bool StoryboardLoaded => storyboard.IsResultAvailable;
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Configuration;
using osu.Game.Rulesets;
@@ -43,7 +42,7 @@ namespace osu.Game.Configuration
{
base.AddBindable(lookup, bindable);
var setting = databasedSettings.FirstOrDefault(s => (int)s.Key == (int)(object)lookup);
var setting = databasedSettings.Find(s => (int)s.Key == (int)(object)lookup);
if (setting != null)
{
bindable.Parse(setting.Value);
+22 -3
View File
@@ -3,6 +3,7 @@
using osu.Framework.Configuration;
using osu.Framework.Configuration.Tracking;
using osu.Framework.Extensions;
using osu.Framework.Platform;
using osu.Game.Overlays;
using osu.Game.Rulesets.Scoring;
@@ -96,15 +97,27 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
Set(OsuSetting.SongSelectRightMouseScroll, false);
Set(OsuSetting.Scaling, ScalingMode.Off);
Set(OsuSetting.ScalingSizeX, 0.8f, 0.2f, 1f);
Set(OsuSetting.ScalingSizeY, 0.8f, 0.2f, 1f);
Set(OsuSetting.ScalingPositionX, 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) : base(storage)
public OsuConfigManager(Storage storage)
: base(storage)
{
}
public override TrackedSettings CreateTrackedSettings() => new TrackedSettings
{
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled"))
new TrackedSetting<bool>(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")),
new TrackedSetting<ScalingMode>(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())),
};
}
@@ -151,6 +164,12 @@ namespace osu.Game.Configuration
BeatmapHitsounds,
IncreaseFirstObjectVisibility,
ScoreDisplayMode,
ExternalLinkWarning
ExternalLinkWarning,
Scaling,
ScalingPositionX,
ScalingPositionY,
ScalingSizeX,
ScalingSizeY,
UIScale
}
}
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.ComponentModel;
namespace osu.Game.Configuration
{
public enum ScalingMode
{
Off,
Everything,
[Description("Excluding overlays")]
ExcludeOverlays,
Gameplay,
}
}
@@ -74,7 +74,7 @@ namespace osu.Game.Graphics.Containers
}
[BackgroundDependencyLoader]
private void load(IBindableBeatmap beatmap)
private void load(IBindable<WorkingBeatmap> beatmap)
{
Beatmap.BindTo(beatmap);
}
@@ -79,6 +79,7 @@ namespace osu.Game.Graphics.Containers
{
AddInternal(new DrawableLinkCompiler(drawables.OfType<SpriteText>().ToList())
{
RelativeSizeAxes = Axes.Both,
TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = action ?? (() =>
{
@@ -122,5 +123,10 @@ namespace osu.Game.Graphics.Containers
}),
});
}
// 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));
}
}
@@ -0,0 +1,161 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Configuration;
using osu.Game.Graphics.Backgrounds;
using osuTK;
namespace osu.Game.Graphics.Containers
{
/// <summary>
/// Handles user-defined scaling, allowing application at multiple levels defined by <see cref="ScalingMode"/>.
/// </summary>
public class ScalingContainer : Container
{
private Bindable<float> sizeX;
private Bindable<float> sizeY;
private Bindable<float> posX;
private Bindable<float> posY;
private readonly ScalingMode? targetMode;
private Bindable<ScalingMode> scalingMode;
private readonly Container content;
protected override Container<Drawable> Content => content;
public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true;
private readonly Container sizableContainer;
private Drawable backgroundLayer;
/// <summary>
/// Create a new instance.
/// </summary>
/// <param name="targetMode">The mode which this container should be handling. Handles all modes if null.</param>
public ScalingContainer(ScalingMode? targetMode = null)
{
this.targetMode = targetMode;
RelativeSizeAxes = Axes.Both;
InternalChild = sizableContainer = new AlwaysInputContainer
{
RelativeSizeAxes = Axes.Both,
RelativePositionAxes = Axes.Both,
CornerRadius = 10,
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]
private void load(OsuConfigManager config)
{
scalingMode = config.GetBindable<ScalingMode>(OsuSetting.Scaling);
scalingMode.ValueChanged += _ => updateSize();
sizeX = config.GetBindable<float>(OsuSetting.ScalingSizeX);
sizeX.ValueChanged += _ => updateSize();
sizeY = config.GetBindable<float>(OsuSetting.ScalingSizeY);
sizeY.ValueChanged += _ => updateSize();
posX = config.GetBindable<float>(OsuSetting.ScalingPositionX);
posX.ValueChanged += _ => updateSize();
posY = config.GetBindable<float>(OsuSetting.ScalingPositionY);
posY.ValueChanged += _ => updateSize();
}
protected override void LoadComplete()
{
base.LoadComplete();
updateSize();
sizableContainer.FinishTransforms();
}
private bool requiresBackgroundVisible => (scalingMode == ScalingMode.Everything || scalingMode == ScalingMode.ExcludeOverlays) && (sizeX.Value != 1 || sizeY.Value != 1);
private void updateSize()
{
if (targetMode == ScalingMode.Everything)
{
// the top level scaling container manages the background to be displayed while scaling.
if (requiresBackgroundVisible)
{
if (backgroundLayer == null)
LoadComponentAsync(backgroundLayer = new Background("Menu/menu-background-1")
{
Colour = OsuColour.Gray(0.1f),
Alpha = 0,
Depth = float.MaxValue
}, d =>
{
AddInternal(d);
d.FadeTo(requiresBackgroundVisible ? 1 : 0, 4000, Easing.OutQuint);
});
else
backgroundLayer.FadeIn(500);
}
else
backgroundLayer?.FadeOut(500);
}
bool scaling = targetMode == null || scalingMode.Value == targetMode;
var targetSize = scaling ? new Vector2(sizeX, sizeY) : Vector2.One;
var targetPosition = scaling ? new Vector2(posX, posY) * (Vector2.One - targetSize) : Vector2.Zero;
bool requiresMasking = scaling && targetSize != Vector2.One;
if (requiresMasking)
sizableContainer.Masking = true;
sizableContainer.MoveTo(targetPosition, 500, Easing.OutQuart);
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;
}
}
}
}
+46 -47
View File
@@ -8,7 +8,6 @@ using osuTK.Graphics;
using osu.Framework.Allocation;
using osu.Framework.Audio;
using osu.Framework.Audio.Sample;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
@@ -33,38 +32,7 @@ namespace osu.Game.Graphics.UserInterface
private readonly Box leftBox;
private readonly Box rightBox;
public virtual string TooltipText
{
get
{
var bindableDouble = CurrentNumber as BindableNumber<double>;
var bindableFloat = CurrentNumber as BindableNumber<float>;
var floatValue = bindableDouble?.Value ?? bindableFloat?.Value;
var floatPrecision = bindableDouble?.Precision ?? bindableFloat?.Precision;
if (floatValue != null)
{
var floatMinValue = bindableDouble?.MinValue ?? bindableFloat.MinValue;
var floatMaxValue = bindableDouble?.MaxValue ?? bindableFloat.MaxValue;
if (floatMaxValue == 1 && (floatMinValue == 0 || floatMinValue == -1))
return floatValue.Value.ToString("P0");
var decimalPrecision = normalise((decimal)floatPrecision, max_decimal_digits);
// Find the number of significant digits (we could have less than 5 after normalize())
var significantDigits = findPrecision(decimalPrecision);
return floatValue.Value.ToString($"N{significantDigits}");
}
var bindableInt = CurrentNumber as BindableNumber<int>;
if (bindableInt != null)
return bindableInt.Value.ToString("N0");
return Current.Value.ToString(CultureInfo.InvariantCulture);
}
}
public virtual string TooltipText { get; private set; }
private Color4 accentColour;
public Color4 AccentColour
@@ -124,6 +92,12 @@ namespace osu.Game.Graphics.UserInterface
AccentColour = colours.Pink;
}
protected override void LoadComplete()
{
updateTooltipText(Current.Value);
base.LoadComplete();
}
protected override bool OnHover(HoverEvent e)
{
Nub.Glowing = true;
@@ -136,21 +110,34 @@ namespace osu.Game.Graphics.UserInterface
base.OnHoverLost(e);
}
protected override void OnUserChange()
protected override bool OnMouseDown(MouseDownEvent e)
{
base.OnUserChange();
playSample();
Nub.Current.Value = true;
return base.OnMouseDown(e);
}
private void playSample()
protected override bool OnMouseUp(MouseUpEvent e)
{
Nub.Current.Value = false;
return base.OnMouseUp(e);
}
protected override void OnUserChange(T value)
{
base.OnUserChange(value);
playSample(value);
updateTooltipText(value);
}
private void playSample(T value)
{
if (Clock == null || Clock.CurrentTime - lastSampleTime <= 50)
return;
if (Current.Value.Equals(lastSampleValue))
if (value.Equals(lastSampleValue))
return;
lastSampleValue = Current.Value;
lastSampleValue = value;
lastSampleTime = Clock.CurrentTime;
sample.Frequency.Value = 1 + NormalizedValue * 0.2f;
@@ -163,16 +150,28 @@ namespace osu.Game.Graphics.UserInterface
sample.Play();
}
protected override bool OnMouseDown(MouseDownEvent e)
private void updateTooltipText(T value)
{
Nub.Current.Value = true;
return base.OnMouseDown(e);
}
if (CurrentNumber.IsInteger)
TooltipText = ((int)Convert.ChangeType(value, typeof(int))).ToString("N0");
else
{
double floatValue = (double)Convert.ChangeType(value, typeof(double));
double floatMinValue = (double)Convert.ChangeType(CurrentNumber.MinValue, typeof(double));
double floatMaxValue = (double)Convert.ChangeType(CurrentNumber.MaxValue, typeof(double));
protected override bool OnMouseUp(MouseUpEvent e)
{
Nub.Current.Value = false;
return base.OnMouseUp(e);
if (floatMaxValue == 1 && floatMinValue >= -1)
TooltipText = floatValue.ToString("P0");
else
{
var decimalPrecision = normalise((decimal)Convert.ChangeType(CurrentNumber.Precision, typeof(decimal)), max_decimal_digits);
// Find the number of significant digits (we could have less than 5 after normalize())
var significantDigits = findPrecision(decimalPrecision);
TooltipText = floatValue.ToString($"N{significantDigits}");
}
}
}
protected override void UpdateAfterChildren()
@@ -160,7 +160,6 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.BottomLeft,
Text = (value as IHasDescription)?.Description ?? (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14,
Font = @"Exo2.0-Bold", // Font should only turn bold when active?
},
Bar = new Box
{
@@ -173,6 +172,8 @@ namespace osu.Game.Graphics.UserInterface
},
new HoverClickSounds()
};
Active.BindValueChanged(val => Text.Font = val ? @"Exo2.0-Bold" : @"Exo2.0", true);
}
protected override void OnActivated() => fadeActive();
@@ -46,7 +46,6 @@ namespace osu.Game.Graphics.UserInterface
Anchor = Anchor.BottomLeft,
Text = (value as Enum)?.GetDescription() ?? value.ToString(),
TextSize = 14,
Font = @"Exo2.0-Bold",
},
box = new Box
{
@@ -59,6 +58,8 @@ namespace osu.Game.Graphics.UserInterface
},
new HoverClickSounds()
};
Active.BindValueChanged(val => Text.Font = val ? @"Exo2.0-Bold" : @"Exo2.0", true);
}
[BackgroundDependencyLoader]
@@ -62,6 +62,6 @@ namespace osu.Game.Graphics.UserInterface
fill.Width = value * UsableWidth;
}
protected override void OnUserChange() => OnSeek?.Invoke(Current);
protected override void OnUserChange(double value) => OnSeek?.Invoke(value);
}
}
@@ -14,7 +14,7 @@ namespace osu.Game.Migrations
{
#pragma warning disable 612, 618
modelBuilder
.HasAnnotation("ProductVersion", "2.1.4-rtm-31024");
.HasAnnotation("ProductVersion", "2.2.0-rtm-35687");
modelBuilder.Entity("osu.Game.Beatmaps.BeatmapDifficulty", b =>
{
@@ -215,6 +215,25 @@ namespace osu.Game.Migrations
b.ToTable("Settings");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Input.Bindings.DatabasedKeyBinding", b =>
{
b.Property<int>("ID")
@@ -239,25 +258,6 @@ namespace osu.Game.Migrations
b.ToTable("KeyBinding");
});
modelBuilder.Entity("osu.Game.IO.FileInfo", b =>
{
b.Property<int>("ID")
.ValueGeneratedOnAdd();
b.Property<string>("Hash");
b.Property<int>("ReferenceCount");
b.HasKey("ID");
b.HasIndex("Hash")
.IsUnique();
b.HasIndex("ReferenceCount");
b.ToTable("FileInfo");
});
modelBuilder.Entity("osu.Game.Rulesets.RulesetInfo", b =>
{
b.Property<int?>("ID")
@@ -454,7 +454,7 @@ namespace osu.Game.Migrations
modelBuilder.Entity("osu.Game.Scoring.ScoreInfo", b =>
{
b.HasOne("osu.Game.Beatmaps.BeatmapInfo", "Beatmap")
.WithMany()
.WithMany("Scores")
.HasForeignKey("BeatmapInfoID")
.OnDelete(DeleteBehavior.Cascade);
+3
View File
@@ -101,6 +101,9 @@ namespace osu.Game.Online.API
//todo: replace this with a ping request.
log.Add(@"In a failing state, waiting a bit before we try again...");
Thread.Sleep(5000);
if (!IsLoggedIn) goto case APIState.Connecting;
if (queue.Count == 0)
{
log.Add(@"Queueing a ping request");
-1
View File
@@ -41,7 +41,6 @@ namespace osu.Game.Online.Chat
/// </summary>
private readonly List<LocalEchoMessage> pendingMessages = new List<LocalEchoMessage>();
/// <summary>
/// An event that fires when new messages arrived.
/// </summary>
+4 -4
View File
@@ -29,8 +29,8 @@ namespace osu.Game.Online.Chat
@"#lobby"
};
private readonly BindableCollection<Channel> availableChannels = new BindableCollection<Channel>();
private readonly BindableCollection<Channel> joinedChannels = new BindableCollection<Channel>();
private readonly BindableList<Channel> availableChannels = new BindableList<Channel>();
private readonly BindableList<Channel> joinedChannels = new BindableList<Channel>();
/// <summary>
/// The currently opened channel
@@ -40,12 +40,12 @@ namespace osu.Game.Online.Chat
/// <summary>
/// The Channels the player has joined
/// </summary>
public IBindableCollection<Channel> JoinedChannels => joinedChannels;
public IBindableList<Channel> JoinedChannels => joinedChannels;
/// <summary>
/// The channels available for the player to join
/// </summary>
public IBindableCollection<Channel> AvailableChannels => availableChannels;
public IBindableList<Channel> AvailableChannels => availableChannels;
private IAPIProvider api;
+7 -3
View File
@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
@@ -30,6 +31,7 @@ namespace osu.Game.Online.Leaderboards
private readonly LoadingAnimation loading;
private ScheduledDelegate showScoresDelegate;
private CancellationTokenSource showScoresCancellationSource;
private bool scoresLoadedOnce;
@@ -49,6 +51,10 @@ namespace osu.Game.Online.Leaderboards
loading.Hide();
// schedule because we may not be loaded yet (LoadComponentAsync complains).
showScoresDelegate?.Cancel();
showScoresCancellationSource?.Cancel();
if (scores == null || !scores.Any())
return;
@@ -58,8 +64,6 @@ namespace osu.Game.Online.Leaderboards
scrollFlow = CreateScoreFlow();
scrollFlow.ChildrenEnumerable = scores.Select((s, index) => CreateDrawableScore(s, index + 1));
// schedule because we may not be loaded yet (LoadComponentAsync complains).
showScoresDelegate?.Cancel();
if (!IsLoaded)
showScoresDelegate = Schedule(showScores);
else
@@ -77,7 +81,7 @@ namespace osu.Game.Online.Leaderboards
}
scrollContainer.ScrollTo(0f, false);
});
}, (showScoresCancellationSource = new CancellationTokenSource()).Token);
}
}
+2 -2
View File
@@ -37,10 +37,10 @@ namespace osu.Game.Online.Multiplayer
public RulesetInfo Ruleset { get; set; }
[JsonIgnore]
public readonly BindableCollection<Mod> AllowedMods = new BindableCollection<Mod>();
public readonly BindableList<Mod> AllowedMods = new BindableList<Mod>();
[JsonIgnore]
public readonly BindableCollection<Mod> RequiredMods = new BindableCollection<Mod>();
public readonly BindableList<Mod> RequiredMods = new BindableList<Mod>();
[JsonProperty("beatmap")]
private APIBeatmap apiBeatmap { get; set; }
+9 -1
View File
@@ -24,7 +24,7 @@ namespace osu.Game.Online.Multiplayer
public Bindable<User> Host { get; private set; } = new Bindable<User>();
[JsonProperty("playlist")]
public BindableCollection<PlaylistItem> Playlist { get; set; } = new BindableCollection<PlaylistItem>();
public BindableList<PlaylistItem> Playlist { get; set; } = new BindableList<PlaylistItem>();
[JsonProperty("channel_id")]
public Bindable<int> ChannelId { get; private set; } = new Bindable<int>();
@@ -79,6 +79,12 @@ namespace osu.Game.Online.Multiplayer
set => MaxAttempts.Value = value;
}
/// <summary>
/// The position of this <see cref="Room"/> in the list. This is not read from or written to the API.
/// </summary>
[JsonIgnore]
public int Position = -1;
public void CopyFrom(Room other)
{
RoomID.Value = other.RoomID;
@@ -103,6 +109,8 @@ namespace osu.Game.Online.Multiplayer
Playlist.AddRange(other.Playlist);
else if (other.Playlist.Count > 0)
Playlist.First().ID = other.Playlist.First().ID;
Position = other.Position;
}
public bool ShouldSerializeRoomID() => false;
+1 -1
View File
@@ -39,7 +39,7 @@ namespace osu.Game.Online
/// <summary>
///
/// </summary>
/// <param name="timeBetweenPolls">The initial time in milliseconds to wait between polls. Setting to zero stops al polling.</param>
/// <param name="timeBetweenPolls">The initial time in milliseconds to wait between polls. Setting to zero stops all polling.</param>
protected PollingComponent(double timeBetweenPolls = 0)
{
TimeBetweenPolls = timeBetweenPolls;
+18 -41
View File
@@ -26,6 +26,7 @@ using osu.Framework.Platform;
using osu.Framework.Threading;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Input;
using osu.Game.Overlays.Notifications;
using osu.Game.Rulesets;
@@ -101,7 +102,7 @@ namespace osu.Game
private OnScreenDisplay onscreenDisplay;
private Bindable<int> configRuleset;
private readonly Bindable<RulesetInfo> ruleset = new Bindable<RulesetInfo>();
private readonly LeasableBindable<RulesetInfo> ruleset = new LeasableBindable<RulesetInfo>(new Bindable<RulesetInfo>());
private Bindable<int> configSkin;
@@ -171,6 +172,7 @@ namespace osu.Game
dependencies.CacheAs(ruleset);
dependencies.CacheAs<IBindable<RulesetInfo>>(ruleset);
dependencies.CacheAs<IMutableBindable<RulesetInfo>>(ruleset);
// bind config int to database RulesetInfo
configRuleset = LocalConfig.GetBindable<int>(OsuSetting.Ruleset);
@@ -187,6 +189,7 @@ namespace osu.Game
}
private ExternalLinkOpener externalLinkOpener;
public void OpenUrlExternally(string url)
{
if (url.StartsWith("/"))
@@ -222,7 +225,7 @@ namespace osu.Game
var databasedSet = BeatmapManager.QueryBeatmapSet(s => s.OnlineBeatmapSetID == beatmap.OnlineBeatmapSetID);
// Use first beatmap available for current ruleset, else switch ruleset.
var first = databasedSet.Beatmaps.FirstOrDefault(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
var first = databasedSet.Beatmaps.Find(b => b.Ruleset == ruleset.Value) ?? databasedSet.Beatmaps.First();
ruleset.Value = first.Ruleset;
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(first);
@@ -353,7 +356,14 @@ namespace osu.Game
ActionRequested = action => volume.Adjust(action),
ScrollActionRequested = (action, amount, isPrecise) => volume.Adjust(action, amount, isPrecise),
},
mainContent = new Container { RelativeSizeAxes = Axes.Both },
screenContainer = new ScalingContainer(ScalingMode.ExcludeOverlays)
{
RelativeSizeAxes = Axes.Both,
},
mainContent = new Container
{
RelativeSizeAxes = Axes.Both,
},
overlayContent = new Container { RelativeSizeAxes = Axes.Both, Depth = float.MinValue },
idleTracker = new IdleTracker(6000)
});
@@ -362,7 +372,7 @@ namespace osu.Game
{
screenStack.ModePushed += screenAdded;
screenStack.Exited += screenRemoved;
mainContent.Add(screenStack);
screenContainer.Add(screenStack);
});
loadComponentSingleFile(Toolbar = new Toolbar
@@ -497,7 +507,7 @@ namespace osu.Game
if (notifications.State == Visibility.Visible)
offset -= ToolbarButton.WIDTH / 2;
screenStack.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
screenContainer.MoveToX(offset, SettingsOverlay.TRANSITION_LENGTH, Easing.OutQuint);
}
settings.StateChanged += _ => updateScreenOffset();
@@ -555,7 +565,7 @@ namespace osu.Game
focused.StateChanged += s =>
{
visibleOverlayCount += s == Visibility.Visible ? 1 : -1;
screenStack.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
screenContainer.FadeColour(visibleOverlayCount > 0 ? OsuColour.Gray(0.5f) : Color4.White, 500, Easing.OutQuint);
};
}
@@ -646,6 +656,7 @@ namespace osu.Game
private OsuScreen currentScreen;
private FrameworkConfigManager frameworkConfig;
private ScalingContainer screenContainer;
protected override bool OnExiting()
{
@@ -678,44 +689,10 @@ namespace osu.Game
{
base.UpdateAfterChildren();
// we only want to apply these restrictions when we are inside a screen stack.
// the use case for not applying is in visual/unit tests.
bool applyBeatmapRulesetRestrictions = !currentScreen?.AllowBeatmapRulesetChange ?? false;
ruleset.Disabled = applyBeatmapRulesetRestrictions;
Beatmap.Disabled = applyBeatmapRulesetRestrictions;
mainContent.Padding = new MarginPadding { Top = ToolbarOffset };
screenContainer.Padding = new MarginPadding { Top = ToolbarOffset };
MenuCursorContainer.CanShowCursor = currentScreen?.CursorVisible ?? false;
}
/// <summary>
/// Sets <see cref="Beatmap"/> while ignoring any beatmap.
/// </summary>
/// <param name="beatmap">The beatmap to set.</param>
public void ForcefullySetBeatmap(WorkingBeatmap beatmap)
{
var beatmapDisabled = Beatmap.Disabled;
Beatmap.Disabled = false;
Beatmap.Value = beatmap;
Beatmap.Disabled = beatmapDisabled;
}
/// <summary>
/// Sets <see cref="Ruleset"/> while ignoring any ruleset restrictions.
/// </summary>
/// <param name="beatmap">The beatmap to set.</param>
public void ForcefullySetRuleset(RulesetInfo ruleset)
{
var rulesetDisabled = this.ruleset.Disabled;
this.ruleset.Disabled = false;
this.ruleset.Value = ruleset;
this.ruleset.Disabled = rulesetDisabled;
}
protected virtual void ScreenChanged(OsuScreen current, Screen newScreen)
{
currentScreen = (OsuScreen)newScreen;
+12 -7
View File
@@ -24,6 +24,7 @@ using osu.Framework.Input;
using osu.Framework.Logging;
using osu.Game.Audio;
using osu.Game.Database;
using osu.Game.Graphics.Containers;
using osu.Game.Input;
using osu.Game.Input.Bindings;
using osu.Game.IO;
@@ -68,8 +69,9 @@ namespace osu.Game
protected override Container<Drawable> Content => content;
private OsuBindableBeatmap beatmap;
protected BindableBeatmap Beatmap => beatmap;
private LeasableBindable<WorkingBeatmap> beatmap;
protected IMutableBindable<WorkingBeatmap> Beatmap => beatmap;
private Bindable<bool> fpsDisplayVisible;
@@ -154,7 +156,6 @@ namespace osu.Game
dependencies.CacheAs<IAPIProvider>(API);
var defaultBeatmap = new DummyWorkingBeatmap(this);
beatmap = new OsuBindableBeatmap(defaultBeatmap, Audio);
dependencies.Cache(RulesetStore = new RulesetStore(contextFactory));
dependencies.Cache(FileStore = new FileStore(contextFactory, Host.Storage));
@@ -173,8 +174,11 @@ namespace osu.Game
// this adds a global reduction of track volume for the time being.
Audio.Track.AddAdjustment(AdjustableProperty.Volume, new BindableDouble(0.8));
dependencies.CacheAs<BindableBeatmap>(beatmap);
dependencies.CacheAs<IBindableBeatmap>(beatmap);
beatmap = new LeasableBindable<WorkingBeatmap>(new OsuBindableBeatmap(defaultBeatmap, Audio));
dependencies.CacheAs<IBindable<WorkingBeatmap>>(beatmap);
dependencies.CacheAs<IMutableBindable<WorkingBeatmap>>(beatmap);
dependencies.CacheAs(beatmap);
FileStore.Cleanup();
@@ -189,7 +193,7 @@ namespace osu.Game
Child = content = new OsuTooltipContainer(MenuCursorContainer.Cursor) { RelativeSizeAxes = Axes.Both }
};
base.Content.Add(new DrawSizePreservingFillContainer { Child = MenuCursorContainer });
base.Content.Add(new ScalingContainer(ScalingMode.Everything) { Child = MenuCursorContainer });
KeyBindingStore.Register(globalBinding);
dependencies.Cache(globalBinding);
@@ -247,7 +251,8 @@ namespace osu.Game
var extension = Path.GetExtension(paths.First())?.ToLowerInvariant();
foreach (var importer in fileImporters)
if (importer.HandledExtensions.Contains(extension)) importer.Import(paths);
if (importer.HandledExtensions.Contains(extension))
importer.Import(paths);
}
public string[] HandledExtensions => fileImporters.SelectMany(i => i.HandledExtensions).ToArray();
@@ -1,6 +1,7 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Linq;
using osuTK;
@@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Chat.Selection
public readonly FillFlowContainer<ChannelListItem> ChannelFlow;
public IEnumerable<IFilterable> FilterableChildren => ChannelFlow.Children;
public IEnumerable<string> FilterTerms => new[] { Header };
public IEnumerable<string> FilterTerms => Array.Empty<string>();
public bool MatchingFilter
{
set
@@ -77,7 +77,6 @@ namespace osu.Game.Overlays.Chat.Tabs
CloseButton.FadeIn(TRANSITION_LENGTH, Easing.OutQuint);
}
protected override void FadeInactive()
{
base.FadeInactive();
@@ -18,7 +18,6 @@ namespace osu.Game.Overlays.KeyBinding
Add(new InGameKeyBindingsSubsection(manager));
}
private class DefaultBindingsSubsection : KeyBindingsSubsection
{
protected override string Header => string.Empty;
@@ -176,7 +176,6 @@ namespace osu.Game.Overlays.Mods
section.DeselectTypes(modTypes, immediate);
}
private SampleChannel sampleOn, sampleOff;
private void modButtonPressed(Mod selectedMod)
+1 -1
View File
@@ -73,7 +73,7 @@ namespace osu.Game.Overlays.Music
}
[BackgroundDependencyLoader]
private void load(BeatmapManager beatmaps, IBindableBeatmap beatmap)
private void load(BeatmapManager beatmaps, IBindable<WorkingBeatmap> beatmap)
{
beatmaps.GetAllUsableBeatmapSets().ForEach(b => addBeatmapSet(b, false, false));
beatmaps.ItemAdded += addBeatmapSet;
+1 -1
View File
@@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Music
private PlaylistList list;
[BackgroundDependencyLoader]
private void load(OsuColour colours, BindableBeatmap beatmap, BeatmapManager beatmaps)
private void load(OsuColour colours, IMutableBindable<WorkingBeatmap> beatmap, BeatmapManager beatmaps)
{
this.beatmap.BindTo(beatmap);
this.beatmaps = beatmaps;
+1 -1
View File
@@ -66,7 +66,7 @@ namespace osu.Game.Overlays
}
[BackgroundDependencyLoader]
private void load(BindableBeatmap beatmap, BeatmapManager beatmaps, OsuColour colours)
private void load(IMutableBindable<WorkingBeatmap> beatmap, BeatmapManager beatmaps, OsuColour colours)
{
this.beatmap.BindTo(beatmap);
this.beatmaps = beatmaps;
+1 -1
View File
@@ -396,11 +396,11 @@ namespace osu.Game.Overlays.Profile
infoTextLeft.NewLine();
infoTextLeft.AddText("Last seen ", lightText);
infoTextLeft.AddText(new DrawableDate(user.LastVisit.Value), boldItalic);
infoTextLeft.NewParagraph();
}
if (user.PlayStyle?.Length > 0)
{
infoTextLeft.NewParagraph();
infoTextLeft.AddText("Plays with ", lightText);
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
}
@@ -49,7 +49,6 @@ namespace osu.Game.Overlays.Profile.Sections.Historical
Api.Queue(request);
}
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
@@ -6,9 +6,14 @@ using System.Drawing;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Configuration;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osuTK.Graphics;
namespace osu.Game.Overlays.Settings.Sections.Graphics
{
@@ -16,24 +21,33 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
{
protected override string Header => "Layout";
private FillFlowContainer letterboxSettings;
private FillFlowContainer<SettingsSlider<float>> scalingSettings;
private Bindable<bool> letterboxing;
private Bindable<ScalingMode> scalingMode;
private Bindable<Size> sizeFullscreen;
private OsuGameBase game;
private SettingsDropdown<Size> resolutionDropdown;
private SettingsEnumDropdown<WindowMode> windowModeDropdown;
private Bindable<float> scalingPositionX;
private Bindable<float> scalingPositionY;
private Bindable<float> scalingSizeX;
private Bindable<float> scalingSizeY;
private const int transition_duration = 400;
[BackgroundDependencyLoader]
private void load(FrameworkConfigManager config, OsuGameBase game)
private void load(FrameworkConfigManager config, OsuConfigManager osuConfig, OsuGameBase game)
{
this.game = game;
letterboxing = config.GetBindable<bool>(FrameworkSetting.Letterboxing);
scalingMode = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling);
sizeFullscreen = config.GetBindable<Size>(FrameworkSetting.SizeFullscreen);
scalingSizeX = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeX);
scalingSizeY = osuConfig.GetBindable<float>(OsuSetting.ScalingSizeY);
scalingPositionX = osuConfig.GetBindable<float>(OsuSetting.ScalingPositionX);
scalingPositionY = osuConfig.GetBindable<float>(OsuSetting.ScalingPositionY);
Container resolutionSettingsContainer;
@@ -49,12 +63,19 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y
},
new SettingsCheckbox
new SettingsSlider<float, UIScaleSlider>
{
LabelText = "Letterboxing",
Bindable = letterboxing,
LabelText = "UI Scaling",
TransferValueOnCommit = true,
Bindable = osuConfig.GetBindable<float>(OsuSetting.UIScale),
KeyboardStep = 0.01f
},
letterboxSettings = new FillFlowContainer
new SettingsEnumDropdown<ScalingMode>
{
LabelText = "Screen Scaling",
Bindable = osuConfig.GetBindable<ScalingMode>(OsuSetting.Scaling),
},
scalingSettings = new FillFlowContainer<SettingsSlider<float>>
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
@@ -62,25 +83,38 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
AutoSizeDuration = transition_duration,
AutoSizeEasing = Easing.OutQuint,
Masking = true,
Children = new Drawable[]
Children = new []
{
new SettingsSlider<double>
new SettingsSlider<float>
{
LabelText = "Horizontal position",
Bindable = config.GetBindable<double>(FrameworkSetting.LetterboxPositionX),
Bindable = scalingPositionX,
KeyboardStep = 0.01f
},
new SettingsSlider<double>
new SettingsSlider<float>
{
LabelText = "Vertical position",
Bindable = config.GetBindable<double>(FrameworkSetting.LetterboxPositionY),
Bindable = scalingPositionY,
KeyboardStep = 0.01f
},
new SettingsSlider<float>
{
LabelText = "Horizontal scale",
Bindable = scalingSizeX,
KeyboardStep = 0.01f
},
new SettingsSlider<float>
{
LabelText = "Vertical scale",
Bindable = scalingSizeY,
KeyboardStep = 0.01f
},
}
},
};
scalingSettings.ForEach(s => bindPreviewEvent(s.Bindable));
var resolutions = getResolutions();
if (resolutions.Count > 1)
@@ -105,16 +139,46 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
}, true);
}
letterboxing.BindValueChanged(isVisible =>
scalingMode.BindValueChanged(mode =>
{
letterboxSettings.ClearTransforms();
letterboxSettings.AutoSizeAxes = isVisible ? Axes.Y : Axes.None;
scalingSettings.ClearTransforms();
scalingSettings.AutoSizeAxes = mode != ScalingMode.Off ? Axes.Y : Axes.None;
if (!isVisible)
letterboxSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
if (mode == ScalingMode.Off)
scalingSettings.ResizeHeightTo(0, transition_duration, Easing.OutQuint);
scalingSettings.ForEach(s => s.TransferValueOnCommit = mode == ScalingMode.Everything);
}, true);
}
/// <summary>
/// Create a delayed bindable which only updates when a condition is met.
/// </summary>
/// <param name="bindable">The config bindable.</param>
/// <returns>A bindable which will propagate updates with a delay.</returns>
private void bindPreviewEvent(Bindable<float> bindable)
{
bindable.ValueChanged += v =>
{
switch (scalingMode.Value)
{
case ScalingMode.Gameplay:
showPreview();
break;
}
};
}
private Drawable preview;
private void showPreview()
{
if (preview?.IsAlive != true)
game.Add(preview = new ScalingPreview());
preview.FadeOutFromOne(1500);
preview.Expire();
}
private IReadOnlyList<Size> getResolutions()
{
var resolutions = new List<Size> { new Size(9999, 9999) };
@@ -132,6 +196,24 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics
return resolutions;
}
private class ScalingPreview : ScalingContainer
{
public ScalingPreview()
{
Child = new Box
{
Colour = Color4.White,
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f,
};
}
}
private class UIScaleSlider : OsuSliderBar<float>
{
public override string TooltipText => base.TooltipText + "x";
}
private class ResolutionSettingsDropdown : SettingsDropdown<Size>
{
protected override OsuDropdown<Size> CreateDropdown() => new ResolutionDropdownControl { Items = Items };
@@ -5,7 +5,6 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Input;
using osu.Framework.Input.Events;
using osu.Game.Configuration;
using osu.Game.Graphics.UserInterface;
@@ -78,66 +77,15 @@ namespace osu.Game.Overlays.Settings.Sections.Input
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()
{
KeyboardStep = 0.01f;
TransferValueOnCommit = true;
}
}
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");
}
}
@@ -1,20 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
namespace osu.Game.Overlays.Settings
{
public class SettingsLabel : SettingsItem<string>
{
protected override Drawable CreateControl() => null;
[BackgroundDependencyLoader]
private void load(OsuColour colour)
{
Colour = colour.Gray6;
}
}
}
+9 -8
View File
@@ -2,7 +2,6 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics.UserInterface;
@@ -23,14 +22,16 @@ namespace osu.Game.Overlays.Settings
RelativeSizeAxes = Axes.X
};
public float KeyboardStep;
[BackgroundDependencyLoader]
private void load()
public bool TransferValueOnCommit
{
var slider = Control as U;
if (slider != null)
slider.KeyboardStep = KeyboardStep;
get => ((U)Control).TransferValueOnCommit;
set => ((U)Control).TransferValueOnCommit = value;
}
public float KeyboardStep
{
get => ((U)Control).KeyboardStep;
set => ((U)Control).KeyboardStep = value;
}
}
}
@@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Toolbar
}
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets, Bindable<RulesetInfo> parentRuleset)
private void load(RulesetStore rulesets, IMutableBindable<RulesetInfo> parentRuleset)
{
this.rulesets = rulesets;
foreach (var r in rulesets.AvailableRulesets)
+1 -1
View File
@@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Edit
}
[BackgroundDependencyLoader]
private void load(IBindableBeatmap beatmap, IFrameBasedClock framedClock)
private void load(IBindable<WorkingBeatmap> beatmap, IFrameBasedClock framedClock)
{
Beatmap.BindTo(beatmap);
+1 -1
View File
@@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Edit
}
[BackgroundDependencyLoader]
private void load(IBindableBeatmap beatmap, IAdjustableClock clock)
private void load(IBindable<WorkingBeatmap> beatmap, IAdjustableClock clock)
{
this.beatmap.BindTo(beatmap);
+1 -1
View File
@@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mods
public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0;
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) => rulesetContainer.SetReplay(CreateReplayScore(rulesetContainer.Beatmap)?.Replay);
public virtual void ApplyToRulesetContainer(RulesetContainer<T> rulesetContainer) => rulesetContainer.SetReplayScore(CreateReplayScore(rulesetContainer.Beatmap));
}
public abstract class ModAutoplay : Mod, IApplicableFailOverride
@@ -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
repeatCount = Math.Max(0, repeatCount - 1);
if (split.Length > 7)
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
+37 -5
View File
@@ -2,8 +2,11 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using osu.Framework.Configuration;
using osu.Framework.Extensions;
using osu.Framework.Extensions.TypeExtensions;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Judgements;
@@ -57,6 +60,11 @@ namespace osu.Game.Rulesets.Scoring
/// </summary>
public readonly BindableInt Combo = new BindableInt();
/// <summary>
/// Create a <see cref="HitWindows"/> for this processor.
/// </summary>
protected virtual HitWindows CreateHitWindows() => new HitWindows();
/// <summary>
/// The current rank.
/// </summary>
@@ -166,7 +174,16 @@ namespace osu.Game.Rulesets.Scoring
score.Accuracy = Math.Round(Accuracy, 4);
score.Rank = Rank;
score.Date = DateTimeOffset.Now;
var hitWindows = CreateHitWindows();
foreach (var result in Enum.GetValues(typeof(HitResult)).OfType<HitResult>().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r)))
score.Statistics[result] = GetStatistic(result);
}
protected abstract int GetStatistic(HitResult result);
public abstract double GetStandardisedScore();
}
public class ScoreProcessor<TObject> : ScoreProcessor
@@ -273,6 +290,8 @@ namespace osu.Game.Rulesets.Scoring
updateScore();
}
private readonly Dictionary<HitResult, int> scoreResultCounts = new Dictionary<HitResult, int>();
/// <summary>
/// Applies the score change of a <see cref="JudgementResult"/> to this <see cref="ScoreProcessor"/>.
/// </summary>
@@ -284,6 +303,9 @@ namespace osu.Game.Rulesets.Scoring
JudgedHits++;
if (result.Type != HitResult.None)
scoreResultCounts[result.Type] = scoreResultCounts.GetOrDefault(result.Type) + 1;
if (result.Judgement.AffectsCombo)
{
switch (result.Type)
@@ -340,20 +362,30 @@ namespace osu.Game.Rulesets.Scoring
if (rollingMaxBaseScore != 0)
Accuracy.Value = baseScore / rollingMaxBaseScore;
switch (Mode.Value)
TotalScore.Value = getScore(Mode.Value);
}
private double getScore(ScoringMode mode)
{
switch (mode)
{
default:
case ScoringMode.Standardised:
TotalScore.Value = max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
break;
return max_score * (base_portion * baseScore / maxBaseScore + combo_portion * HighestCombo / maxHighestCombo) + bonusScore;
case ScoringMode.Classic:
// should emulate osu-stable's scoring as closely as we can (https://osu.ppy.sh/help/wiki/Score/ScoreV1)
TotalScore.Value = bonusScore + baseScore * (1 + Math.Max(0, HighestCombo - 1) / 25);
break;
return bonusScore + baseScore * (1 + Math.Max(0, HighestCombo - 1) / 25);
}
}
protected override int GetStatistic(HitResult result) => scoreResultCounts.GetOrDefault(result);
public override double GetStandardisedScore() => getScore(ScoringMode.Standardised);
protected override void Reset(bool storeResults)
{
scoreResultCounts.Clear();
if (storeResults)
{
MaxHits = JudgedHits;
+1 -1
View File
@@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.UI
private WorkingBeatmap beatmap;
[BackgroundDependencyLoader]
private void load(IBindableBeatmap beatmap)
private void load(IBindable<WorkingBeatmap> beatmap)
{
this.beatmap = beatmap.Value;
}
+8 -7
View File
@@ -22,6 +22,7 @@ using osu.Game.Overlays;
using osu.Game.Replays;
using osu.Game.Rulesets.Configuration;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
namespace osu.Game.Rulesets.UI
{
@@ -130,7 +131,7 @@ namespace osu.Game.Rulesets.UI
protected virtual ReplayInputHandler CreateReplayInputHandler(Replay replay) => null;
public Replay Replay { get; private set; }
public Score ReplayScore { get; private set; }
/// <summary>
/// Whether the game is paused. Used to block user input.
@@ -140,14 +141,14 @@ namespace osu.Game.Rulesets.UI
/// <summary>
/// Sets a replay to be used, overriding local input.
/// </summary>
/// <param name="replay">The replay, null for local input.</param>
public virtual void SetReplay(Replay replay)
/// <param name="replayScore">The replay, null for local input.</param>
public virtual void SetReplayScore(Score replayScore)
{
if (ReplayInputManager == null)
throw new InvalidOperationException($"A {nameof(KeyBindingInputManager)} which supports replay loading is not available");
Replay = replay;
ReplayInputManager.ReplayInputHandler = replay != null ? CreateReplayInputHandler(replay) : null;
ReplayScore = replayScore;
ReplayInputManager.ReplayInputHandler = replayScore != null ? CreateReplayInputHandler(replayScore.Replay) : null;
HasReplayLoaded.Value = ReplayInputManager.ReplayInputHandler != null;
}
@@ -302,9 +303,9 @@ namespace osu.Game.Rulesets.UI
mod.ReadFromConfig(config);
}
public override void SetReplay(Replay replay)
public override void SetReplayScore(Score replayScore)
{
base.SetReplay(replay);
base.SetReplayScore(replayScore);
if (ReplayInputManager?.ReplayInputHandler != null)
ReplayInputManager.ReplayInputHandler.GamefieldToScreenSpace = Playfield.GamefieldToScreenSpace;
+1 -1
View File
@@ -31,7 +31,7 @@ namespace osu.Game.Scoring
[Column(TypeName="DECIMAL(1,4)")]
public double Accuracy { get; set; }
[JsonIgnore]
[JsonProperty(@"pp")]
public double? PP { get; set; }
[JsonProperty("max_combo")]
@@ -0,0 +1,27 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osuTK.Graphics;
namespace osu.Game.Screens.Backgrounds
{
public class BackgroundScreenBlack : BackgroundScreen
{
public BackgroundScreenBlack()
{
Child = new Box
{
Colour = Color4.Black,
RelativeSizeAxes = Axes.Both,
};
}
protected override void OnEntering(Screen last)
{
Show();
}
}
}
@@ -2,10 +2,14 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.MathUtils;
using osu.Framework.Threading;
using osu.Game.Graphics.Backgrounds;
using osu.Game.Online.API;
using osu.Game.Skinning;
using osu.Game.Users;
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 Bindable<User> user;
private Bindable<Skin> skin;
[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);
display(new Background(backgroundName));
Next();
}
private void display(Background newBackground)
@@ -39,8 +53,33 @@ namespace osu.Game.Screens.Backgrounds
nextTask?.Cancel();
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);
}
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;
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More